Packages


library(dplyr)
library(ggcorrplot)
library(ggpubr)
library(patchwork)
# library("here")
# library("bookdown")
# library("downloadthis")
library(vegan)
library(plyr)
library(e1071)
library(tidyverse)
library(viridis)
library(GGally)
library(ggrepel)
library(readr)
library(RColorBrewer)
library(oce) 
library(plotly)
library(purrr)
library(furrr)

#install_github("jfq3/ggordiplots")
library(ggordiplots)


source("gg_ordisurf_viridis.R")

LOAD OBJECTS IF HAVE RUN ALREADY

geodist_pa<-readRDS(file.path(dataPath,"inputs/Barents_geodist_pa.rds"))
geodist_r6<-readRDS(file.path(dataPath,"inputs/Barents_geodist_r6.rds"))

mds_pa<-readRDS(file.path(dataPath,"inputs/Barents_mds_pa.rds"))
mds_r6<-readRDS(file.path(dataPath,"inputs/Barents_mds_r6.rds"))
#mds_r6_1000<-readRDS(file.path(dataPath,"inputs/Barents_mds_r6_1000rep.rds"))

ep<-0.8 #epsilon (to avoid having to find and run just this line from code blocks where the mds objects were made)

Data

env_sort <- read.csv(file.path(dataPath,"inputs/SPLITS/BARENTS/LDnoSBhi02_env_sort_2022-09-29.csv")) %>% as.data.frame
otu_sort <- read.csv(file.path(dataPath,"inputs/SPLITS/BARENTS/LDnoSBhi02_otu_sort_2022-09-29.csv")) %>% as.data.frame


#must be same length and equal to unique sample number length - the ordered lists are assumed to be directly relatable on a row by row basis

joinedDat<- left_join(env_sort,otu_sort, by=c("SampID"="SampID"))
joinedDat1<-subset(joinedDat, bathy <= -99)
summary(joinedDat1$bathy)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -498.5  -289.4  -251.7  -251.4  -204.5  -100.4 
env_sort<-joinedDat1 %>% select(c(2:351))
otu_sort<-joinedDat1 %>% select(c(2,352:491))

remove any variables with not enough coverage


env_sort <- env_sort %>% select(-c("BO22_lightbotmean_bdmean",
                               "BO22_lightbotltmax_bdmean",
                               "BO22_lightbotltmin_bdmean",
                               "BO22_lightbotrange_bdmean"))

Plot map to check location of samples

env_sort_locations<- ggplot(data = env_sort,
                      aes(x = X.y,
                          y = Y)) +
  theme_classic() +
  geom_point(aes(colour = bathy),
             size = 2) +
  scale_colour_gradient2(low = "red",
                         mid = "yellow",
                         high = "green") +
  ggtitle("Location of samples in sorted env file")

env_sort_locations

Data cleaning

## Removing NAs
otuCompl <- otu_sort[complete.cases(env_sort[, -c(1:which(colnames(env_sort)=="bathy")-1)]), ] # make sure we have the columms 
envCompl <- env_sort[complete.cases(env_sort[, -c(1:which(colnames(env_sort)=="bathy")-1)]), ]

## Removing observations with less than 4 OTUs
sel <- rowSums(otuCompl[, -c(1:2)]) >= 4
otuSel <- otuCompl[sel, ]
envSel <- envCompl[sel, ]


## Removing phosphate
#envSel <- envSel %>% select(-phosphate_mean.tif)


dim(otuSel); dim(envSel)
[1] 977 141
[1] 977 346

Show what samples are left after complete cases and >4 OTUs filters

env_sel_locations<- ggplot(data = envSel,
                      aes(x = X.y,
                          y = Y)) +
  theme_classic() +
  geom_point(aes(colour = bathy),
             size = 2) +
  scale_colour_gradient2(low = "red",
                         mid = "yellow",
                         high = "green") +
  ggtitle("Location of samples in sorted env file")

env_sel_locations

Show what got removed in complete cases filter

envCompl_ccrem<-env_sort%>%filter(!SampID%in%envCompl$SampID)

envCompl_ccrem_locations<- ggplot(data = envCompl_ccrem,
                      aes(x = X.y,
                          y = Y)) +
  theme_classic() +
  geom_point(aes(colour = bathy),
             size = 2) +
  scale_colour_gradient2(low = "red",
                         mid = "yellow",
                         high = "green") +
  ggtitle("Location of removed complete case samples resulting in envSel file")

envCompl_ccrem_locations

Show what got removed in <4 OTUs filter

inv.sel <- rowSums(otuCompl[, -c(1:2)]) < 4
env.invSel <- envCompl[inv.sel, ]

env.invSel_locations<- ggplot(data = env.invSel,
                      aes(x = X.y,
                          y = Y)) +
  theme_classic() +
  geom_point(aes(colour = bathy),
             size = 2) +
  scale_colour_gradient2(low = "red",
                         mid = "yellow",
                         high = "green") +
  ggtitle("Location of removed samples with <4 OTUs resulting in envSel file")

env.invSel_locations

[Optional] Data thinning

# otu_red <- otu1[1:500, ]
# otu <- otu_red
# 
# env_red <- env[1:500, ]
# env <- env_red

Whole dataset for first run


otu<-otuSel[,-c(1: which(colnames(otuSel)=="Lithodidae")-1)]
env<-envSel[,-c(1: which(colnames(env_sort)=="bathy")-1)]

table(is.na(otu))

 FALSE 
136780 
table(is.na(env))

 FALSE 
321433 
#str(otuSel)
#str(envSel)

Splitting in subsets

# ## Class 1
# otu1 <- subset(otuSel, envSel$SplitRev == 1)
# env1 <- envSel %>% filter(SplitRev == 1)
# 
# ## Class 2
# otu2 <- subset(otuSel, envSel$SplitRev == 2)
# env2 <- envSel %>% filter(SplitRev == 2)
# 
# ## Class 4
# otu3 <- subset(otuSel, envSel$SplitRev == 3)
# env3 <- envSel %>% filter(SplitRev == 3)
# 
# ## Class 4
# otu4 <- subset(otuSel, envSel$SplitRev == 4)
# env4 <- envSel %>% filter(SplitRev == 4)
# 
# ## Class 6
# otu6 <- subset(otuSel, envSel$SplitRev == 6)
# env6 <- envSel %>% filter(SplitRev == 6)
# 
# ## Class 7
# otu7 <- subset(otuSel, envSel$SplitRev == 7)
# env7 <- envSel %>% filter(SplitRev == 7)
# 
# ## Class 8
# otu8 <- subset(otuSel, envSel$SplitRev == 8)
# env8 <- envSel %>% filter(SplitRev == 8)

Selecting subset

# ## Selecting 
# otu <- otu2
# env <- env2

format data correctly

#env$coords.x1<-as.numeric(env$coords.x1)
#env$coords.x2<-as.numeric(env$coords.x2)

Abundance weighting

Make function You might have to drop variables that have been imported as character


otu_pa <- decostand(x = otu[, -c(1)],
                    method = "pa")

# y = ax^w         # power transformation formula
dt <- otu[, -c(1:2)]          # species data to transform
x_mn <- min(dt[dt > 0])
x_mx <- max(dt)
rng <- 6           # abundance range
w <- log(rng) / (log(x_mx) - log(x_mn))
a <- x_mn^(-w)
# otu_6 <- a * dt[, -c(1:3)]^w
otu_6 <- a * dt^w
range(otu_6)
[1] 0 6

Ordination methods

DCA PA

Sys.time()
[1] "2022-09-29 18:25:00 CEST"
dca_pa <- decorana(veg = otu_pa)
Warning: some species were removed because they were missing in the data
print(dca_pa, head=T)

Call:
decorana(veg = otu_pa) 

Detrended correspondence analysis with 26 segments.
Rescaling of axes with 4 iterations.

                  DCA1   DCA2   DCA3   DCA4
Eigenvalues     0.4930 0.3926 0.2994 0.2039
Decorana values 0.8128 0.3817 0.2571 0.2339
Axis lengths    3.6589 4.0737 3.5442 2.8505

GNMDS PA

Distances - don’t run if loading saved object

## Bray-Curtis
dist_pa <- vegdist(x = otu_pa, method = "bray")

## Geodist
ep <- 0.8     # epsilon
geodist_pa <- isomapdist(dist = dist_pa, epsilon = ep)
Save the result - don’t run if loading saved object
saveRDS(geodist_pa,
        file = (file.path(dataPath,"inputs/Barents_geodist_pa.rds")))

Ordination

monoMDS - don’t run if loading saved object

took 6.4mins with jonatan’s paralell solution (usually ~10mins)

# 
# Sys.time()
# d <- 2
# mds_pa <- list()
# 
# for (i in 1:100) {
#   mds_pa[[i]]<-monoMDS(geodist_pa,
#                     matrix(c(runif(dim(otu_pa)[1]*d)),
#                            nrow = dim(otu_pa)[1]),
#                     k = d,
#                     model = "global",
#                     maxit = 2000,
#                     smin = 1e-7,
#                     sfgrmin = 1e-7)
# }
# Sys.time()


## monoMDS
# d <- 2
# mds_r6 <- list()
# 
# Sys.time()
# for (i in 1:200) {
#   mds_r6[[i]]<-monoMDS(geodist_r6,
#                     matrix(c(runif(dim(otu_6)[1]*d)),
#                            nrow = dim(otu_6)[1]),
#                     k = d,
#                     model = "global",
#                     maxit = 2000,
#                     smin = 1e-7,
#                     sfgrmin = 1e-7)
# }
# Sys.time()

#Jonatan's upgrade for speed - parallel processing of mds reptitions
library(furrr) # Package to run non sequential functions in parallel
#library(purrr)
plan(multisession)

d <- 2

i <-200 # number of reps

List_geodist_pa <- lapply(seq_len(i), function(X) geodist_pa) # makes a list with the input data repeated as many times as reps wanted
start_t<-Sys.time()
Xmds_pa<-furrr::future_map(List_geodist_pa, 
                  function(x) monoMDS(x,
                                      matrix(c(runif(dim(otu_6)[1]*d)),
                                             nrow = dim(otu_6)[1]),
                                      k = d,
                                      model = "global",
                                      maxit = 2000,
                                      smin = 1e-7,
                                      sfgrmin = 1e-7),
                  .progress = TRUE)
Sys.time() - start_t
Save the result - don’t run if loading saved object

can take a few mins

saveRDS(Xmds_pa,
        file = file.path(dataPath,"inputs/Barents_mds_barents_pa.rds"))
Best nmds solution - PA

Make function?

# Loading geodist object
# geodist_nfi <- readRDS(file = "../SplitRev2_geodist_pa_full.rds")
# Loading mds results
# mds <- readRDS(file = "../SplitRev2_mds_pa_full.rds")

## Extracting the stress of each nmds iteration
mds_stress_pa<-unlist(lapply(Xmds_pa, function(v){v[[22]]})) 

ordered_pa <-order(mds_stress_pa)

## Best, second best, and worst solution
mds_stress_pa[ordered_pa[1]]
mds_stress_pa[ordered_pa[2]]
mds_stress_pa[ordered_pa[10]]

## Scaling of axes to half change units and varimax rotation by postMDS
mds_best_pa<-postMDS(Xmds_pa[[ordered_pa[1]]],
                  geodist_pa, 
                  pc = TRUE, 
                  halfchange = TRUE, 
                  threshold = ep)     # Is this threshold related to the epsilon above?
mds_best_pa

mds_secbest_pa <- postMDS(Xmds_pa[[ordered_pa[2]]],
                          geodist_pa, 
                          pc = TRUE, 
                          halfchange = TRUE, 
                          threshold = ep)
mds_secbest_pa

## Procrustes comparisons
procr_pa <- procrustes(mds_best_pa,
                    mds_secbest_pa,
                    permutations=999)
protest(mds_best_pa,
        mds_secbest_pa,
        permutations=999)

plot(procr_pa)

png(file=file.path(dataPath,"outputs/Barents_procrustes_pa.png"), width=1000, height=700)
plot(procr_pa)
dev.off()
Correlation of axis: DCA vs NMDS - PA
# Extracting ordination axis
ax <- 2
axis_pa <- cbind(mds_best_pa$points,
                 scores(dca_pa,
                        display = "sites",
                        origin = TRUE)[, 1:ax])

ggcorr(axis_pa, 
       method=c("everything","kendall"), 
       label = TRUE,
       label_size = 3, 
       label_color = "black",  
       nbreaks = 8,
       label_round = 3,
       low = "red",
       mid = "white",
       high = "green")
Save the figure
ggsave(filename = file.path(dataPath,"outputs/Barents_correlationPCAvsNMDS_PA.png"),
       device = "png",
       dpi=300 )

# Switching direction of NMDS1
# mds_best$points[, 1] <- -mds_best$points[, 1]

DCA R6

dca_r6 <- decorana(veg = otu_6)
Warning: some species were removed because they were missing in the data
print(dca_r6, head=T)

Call:
decorana(veg = otu_6) 

Detrended correspondence analysis with 26 segments.
Rescaling of axes with 4 iterations.

                  DCA1   DCA2   DCA3   DCA4
Eigenvalues     0.4478 0.3736 0.2680 0.2816
Decorana values 0.4613 0.3696 0.2924 0.2686
Axis lengths    4.2345 4.9928 4.7145 3.0913

GNMDS R6

Distances - don’t run if loading saved object

## Bray-Curtis
dist_r6 <- vegdist(x = otu_6, method = "bray")

## Geodist
ep <- 0.80     # epsilon
geodist_r6 <- isomapdist(dist = dist_r6, epsilon = ep)
Save the result - don’t run if loading saved object
saveRDS(geodist_r6,
        file = (file.path(dataPath,"inputs/Barents_below100_geodist_r6.rds")))

Ordination

200 reps - don’t run if loading saved object

Jonatan’s parallel solution took 5.4mins, before it took 10mins

## monoMDS
# d <- 2
# mds_r6 <- list()
# 
# Sys.time()
# for (i in 1:200) {
#   mds_r6[[i]]<-monoMDS(geodist_r6,
#                     matrix(c(runif(dim(otu_6)[1]*d)),
#                            nrow = dim(otu_6)[1]),
#                     k = d,
#                     model = "global",
#                     maxit = 2000,
#                     smin = 1e-7,
#                     sfgrmin = 1e-7)
# }
# Sys.time()

#Jonatan's upgrade for speed - parallel processing of mds reptitions
#library(furrr) # Package to run non sequential functions in parallel
#library(purrr)
plan(multisession)

d <- 2

i <-200 # number of reps

List_geodist_r6 <- lapply(seq_len(i), function(X) geodist_r6) # makes a list with the input data repeated as many times as reps wanted
start_t<-Sys.time()
Xmds_r6<-furrr::future_map(List_geodist_r6, 
                  function(x) monoMDS(x,
                                      matrix(c(runif(dim(otu_6)[1]*d)),
                                             nrow = dim(otu_6)[1]),
                                      k = d,
                                      model = "global",
                                      maxit = 2000,
                                      smin = 1e-7,
                                      sfgrmin = 1e-7),
                  .progress = TRUE)

 Progress: ──────                                                           100%
 Progress: ────────                                                         100%
 Progress: ──────────                                                       100%
 Progress: ────────────                                                     100%
 Progress: ──────────────                                                   100%
 Progress: ────────────────                                                 100%
 Progress: ──────────────────                                               100%
 Progress: ───────────────────                                              100%
 Progress: ─────────────────────                                            100%
 Progress: ───────────────────────                                          100%
 Progress: ─────────────────────────                                        100%
 Progress: ────────────────────────────                                     100%
 Progress: ──────────────────────────────                                   100%
 Progress: ────────────────────────────────                                 100%
 Progress: ──────────────────────────────────                               100%
 Progress: ────────────────────────────────────                             100%
 Progress: ──────────────────────────────────────                           100%
 Progress: ───────────────────────────────────────                          100%
 Progress: ─────────────────────────────────────────                        100%
 Progress: ───────────────────────────────────────────                      100%
 Progress: ─────────────────────────────────────────────                    100%
 Progress: ───────────────────────────────────────────────                  100%
 Progress: ─────────────────────────────────────────────────                100%
 Progress: ──────────────────────────────────────────────────               100%
 Progress: ────────────────────────────────────────────────────             100%
 Progress: ───────────────────────────────────────────────────────          100%
 Progress: ────────────────────────────────────────────────────────         100%
 Progress: ─────────────────────────────────────────────────────────        100%
 Progress: ────────────────────────────────────────────────────────────     100%
 Progress: ─────────────────────────────────────────────────────────────    100%
 Progress: ──────────────────────────────────────────────────────────────   100%
 Progress: ──────────────────────────────────────────────────────────────   100%
 Progress: ───────────────────────────────────────────────────────────────  100%
 Progress: ───────────────────────────────────────────────────────────────  100%
 Progress: ──────────────────────────────────────────────────────────────── 100%
 Progress: ──────────────────────────────────────────────────────────────── 100%
 Progress: ──────────────────────────────────────────────────────────────── 100%
 Progress: ──────────────────────────────────────────────────────────────── 100%
 Progress: ──────────────────────────────────────────────────────────────── 100%
 Progress: ──────────────────────────────────────────────────────────────── 100%
 Progress: ──────────────────────────────────────────────────────────────── 100%
 Progress: ──────────────────────────────────────────────────────────────── 100%
 Progress: ──────────────────────────────────────────────────────────────── 100%Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".Warning: UNRELIABLE VALUE: Future (‘<none>’) unexpectedly generated random numbers without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound and the overall results might be invalid. To fix this, specify 'seed=TRUE'. This ensures that proper, parallel-safe random numbers are produced via the L'Ecuyer-CMRG method. To disable this check, use 'seed=NULL', or set option 'future.rng.onMisuse' to "ignore".
Sys.time() - start_t
Time difference of 1.793691 mins
Save the result - don’t run if loading saved object
saveRDS(Xmds_r6,
        file = file.path(dataPath,"inputs/Barents_below100_mds_r6.rds")) 
Best nmds solution r6 200 rep
# Loading geodist object
# geodist_nfi <- readRDS(file = "../SplitRev2_geodist_nfi.rds")

# Loading mds results
# mds <- readRDS(file = "../SplitRev2_mds.rds")

## Extracting the stress of each nmds iteration
mds_stress_r6<-unlist(lapply(Xmds_r6, function(v){v[[22]]})) 

ordered_r6 <-order(mds_stress_r6)

## Best, second best, and worst solution
mds_stress_r6[ordered_r6[1]]
[1] 0.2544858
mds_stress_r6[ordered_r6[2]]
[1] 0.2544858
mds_stress_r6[ordered_r6[100]]
[1] 0.2554883
## Scaling of axes to half change units and varimax rotation by postMDS
mds_best_r6<-postMDS(Xmds_r6[[ordered_r6[1]]],
                     geodist_r6, 
                     pc = TRUE, 
                     halfchange = TRUE, 
                     threshold = ep)     # Is this threshold related to the epsilon above?
mds_best_r6

Call:
monoMDS(dist = x, y = matrix(c(runif(dim(otu_6)[1] * d)), nrow = dim(otu_6)[1]),      k = d, model = "global", maxit = 2000, smin = 1e-07, sfgrmin = 1e-07) 

Non-metric Multidimensional Scaling

977 points, dissimilarity ‘bray shortest isomap’, call ‘isomapdist(dist = dist_r6, epsilon = ep)’

Dimensions: 2 
Stress:     0.2544858 
Stress type 1, weak ties
Scores scaled to unit root mean square, rotated to principal components
Stopped after 93 iterations: Scale factor of gradient nearly zero (< sfgrmin)
mds_secbest_r6<-postMDS(Xmds_r6[[ordered_r6[2]]],
                        geodist_r6, 
                        pc = TRUE, 
                        halfchange = TRUE, 
                        threshold = ep)
mds_secbest_r6

Call:
monoMDS(dist = x, y = matrix(c(runif(dim(otu_6)[1] * d)), nrow = dim(otu_6)[1]),      k = d, model = "global", maxit = 2000, smin = 1e-07, sfgrmin = 1e-07) 

Non-metric Multidimensional Scaling

977 points, dissimilarity ‘bray shortest isomap’, call ‘isomapdist(dist = dist_r6, epsilon = ep)’

Dimensions: 2 
Stress:     0.2544858 
Stress type 1, weak ties
Scores scaled to unit root mean square, rotated to principal components
Stopped after 195 iterations: Stress nearly unchanged (ratio > sratmax)
## Procrustes comparisons
procr_r6 <- procrustes(mds_best_r6,
                       mds_secbest_r6,
                       permutations=999)
protest(mds_best_r6,
        mds_secbest_r6,
        permutations=999)

Call:
protest(X = mds_best_r6, Y = mds_secbest_r6, permutations = 999) 

Procrustes Sum of Squares (m12 squared):        1.588e-05 
Correlation in a symmetric Procrustes rotation:     1 
Significance:  0.001 

Permutation: free
Number of permutations: 999
plot(procr_r6)

png(file.path(dataPath,"outputs/Barents_below100_procrustes_r6.png"), width=1000, height=700,) #added 1000
plot(procr_r6)
dev.off()
png 
  2 

#### 1000 reps - don’t run if loading saved object Currently commented out as it gained nothing but took extra time. Can be removed in due course.

Correlation of axis: DCA vs NMDS

retain the 200 rep version

# Extracting ordination axis
ax <- 2
axis_r6 <- cbind(mds_best_r6$points,
                 scores(dca_r6,
                        display = "sites",
                        origin = TRUE)[, 1:ax])

ggcorr(axis_r6, 
       method=c("everything","kendall"), 
       label = TRUE,
       label_size = 3, 
       label_color = "black",  
       nbreaks = 8,
       label_round = 3,
       low = "red",
       mid = "white",
       high = "green")

Save the figure
ggsave(filename = file.path(dataPath,"outputs/Barents_below100_correlationPCAvsNMDS_r6.png"),
       device = "png",
       dpi=300 )
Saving 7 x 7 in image
# Switching direction of NMDS1
# mds_best$points[, 1] <- -mds_best$points[, 1]

Plotting DCA & GNMDS

## Adding scores to data frame
otu_6$gnmds1 <- mds_best_r6$points[, 1]
otu_6$gnmds2 <- mds_best_r6$points[, 2]
otu_6$dca1 <- scores(dca_r6, display = "sites", origin = TRUE)[, 1]
otu_6$dca2 <- scores(dca_r6, display = "sites", origin = TRUE)[, 2]

p_gnmds_r6 <- ggplot(data = otu_6,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS",
          subtitle = "First run") +
  geom_point(colour = "red") +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")

p_dca_r6 <- ggplot(data = otu_6,
                   aes(x = dca1,
                       y = dca2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("DCA",
          subtitle = "First run") +
  geom_point(colour = "red") +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")

p_gnmds_r6 + p_dca_r6

NA
NA
Save the figure
ggsave(file.path(dataPath,"outputs/Barents_below100_gnmds_dca_r6.png"),
       device = "png", 
       dpi=300)
Saving 7 x 7 in image

Species-environment relationships

Selecting ordination

ord <- mds_best_r6

## Axis scores if selected ord is GNMDS
axis <- ord$points %>% as.data.frame

## Axis scores if selected ord is DCA
# axis <- scores(ord,
#                display = "sites",
#                origin = TRUE)[, 1:ax])

Create additional variables

decided to make MLD-bathy vars

env<-env %>% 
  mutate ("MLDmean_bathy"=MLDmean_Robinson-(bathy*-1),
          "MLDmin_bathy"=MLDmin_Robinson-(bathy*-1),
          "MLDmax_bathy"=MLDmax_Robinson-(bathy*-1))

env$MLDmean_bathy<-cut(env$MLDmean_bathy, 
      breaks=c(-2560, -20,20,130),#checked range of values first (min -2554, max 123)
      labels=c('belowMLD','onPycno','inMixLayer'))
env$MLDmin_bathy<-cut(env$MLDmin_bathy, 
      breaks=c(-2560, -20,20,130),#checked range of values first (min -2554, max 123)
      labels=c('belowMLD','onPycno','inMixLayer'))
env$MLDmax_bathy<-cut(env$MLDmax_bathy, 
      breaks=c(-2560, -20,20,130),#checked range of values first (min -2554, max 123)
      labels=c('belowMLD','onPycno','inMixLayer'))

env$swDensRob_avs<-swRho(salinity=env$Smean_Robinson,
                         temperature=env$Tmean_Robinson,
                         pressure=(env$bathy*-1),
                         eos="unesco")

Correlation ordination axes and environmental variables

Removing non-env vars

env_cont<-env%>% select(-c(landscape,sedclass,gmorph, MLDmean_bathy, MLDmax_bathy, MLDmin_bathy, #categorical
                           optional, #not a var
                           MLDmax_Robinson, MLDmean_Robinson, MLDmin_Robinson,  #replaced by new vars
                           MLDsd_Robinson #not meaningful
                           ))
env_cont<-env_cont%>% mutate_if(is.integer,as.numeric)

env_corr <- env_cont # %>% select(-c(SampID))

# env_corr$coords.x1<-as.numeric(env_corr$coords.x1)
# env_corr$coords.x2<-as.numeric(env_corr$coords.x2)

env_corr[(!is.numeric(env_corr)),]

Correlations

# Vector to hold correlations
cor_ax1 <- NULL
cor_ax2 <- NULL
pv_ax1 <- NULL
pv_ax2 <- NULL

# NMDS1
for( i in seq(length(env_corr))) {
  ct.i <- cor.test(axis$MDS1,
                   env_corr[, i],
                   method = "kendall")
  cor_ax1[i] <- ct.i$estimate
  pv_ax1[i] <- ct.i$p.value
}
Warning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zero
# NMDS2
for( i in seq(length(env_corr))) {
  ct.i <- cor.test(axis$MDS2,
                   env_corr[, i],
                   method = "kendall")
  cor_ax2[i] <- ct.i$estimate
  pv_ax2[i] <- ct.i$p.value
}
Warning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zero
cor_tab <- data.frame(env = names(env_corr),
                      ord_ax1 = cor_ax1,
                      pval_ax1 = pv_ax1,
                      ord_ax2 = cor_ax2,
                      pval_ax2 = pv_ax2)

cor_tab

write.csv(x = cor_tab,
          file = file.path(dataPath,"inputs/Barents_below100_cor-table_r6_200rep_MLD-bathy.csv"),
          row.names = FALSE)

Dot chart to check for gaps in correlation

cor_a1_sort<-cor_tab%>%
  mutate(abs_ord_ax1=abs(ord_ax1),
         abs_ord_ax2=abs(ord_ax2)) %>%
  arrange(desc(abs_ord_ax1))

cor_a2_sort<-cor_tab%>%
  mutate(abs_ord_ax1=abs(ord_ax1),
         abs_ord_ax2=abs(ord_ax2)) %>%
  arrange(desc(abs_ord_ax2))

dotchart(cor_a1_sort$abs_ord_ax1, main="Absolute (+/-) correlations between envVars and gnmds axis 1")


cor_cut<-0.38 #decide

cor_sel<-subset(cor_a1_sort,abs_ord_ax1>cor_cut)
cor_sel

as.data.frame(cor_sel$env)

Sel env var (top corr)

env_os <- env[, cor_sel$env]
env_os
str(env_os)
'data.frame':   977 obs. of  20 variables:
 $ BO22_dissoxltmax_bdmean: num  329 329 330 328 328 ...
 $ BO22_dissoxltmin_bdmean: num  291 291 292 290 290 ...
 $ BO22_dissoxmean_bdmean : num  310 310 311 309 309 ...
 $ BO22_dissoxrange_bdmean: num  310 310 311 309 309 ...
 $ temp_min               : num  0.293 0.289 0.188 0.356 0.339 ...
 $ BO22_icethickmean_ss   : num  1.72e-06 1.77e-06 3.98e-06 5.60e-07 6.01e-07 ...
 $ BO22_icethickltmax_ss  : num  1.68e-05 1.70e-05 2.95e-05 5.41e-06 5.57e-06 ...
 $ BO22_icecoverltmax_ss  : num  3.89e-06 3.96e-06 7.49e-06 1.56e-06 1.60e-06 ...
 $ BO22_icecovermean_ss   : num  5.55e-07 5.82e-07 1.01e-06 0.00 0.00 ...
 $ Tmin_Robinson          : num  3.77 3.76 3.56 4.1 4.09 ...
 $ temp_mean              : num  1.59 1.58 1.26 1.86 1.85 ...
 $ Y                      : num  8125334 8125534 8137534 8112534 8112934 ...
 $ coords.x2              : num  8125285 8125495 8137580 8112629 8112862 ...
 $ BO22_icecoverrange_ss  : num  3.97e-05 4.02e-05 7.06e-05 1.62e-05 1.65e-05 ...
 $ BO22_icethickrange_ss  : num  1.53e-04 1.56e-04 2.74e-04 4.96e-05 5.07e-05 ...
 $ Tmean_Robinson         : num  4.07 4.06 3.9 4.27 4.26 ...
 $ temp_max               : num  3.82 3.82 3.92 3.78 3.78 ...
 $ Leieschara_sp.         : num  0 0 0 0 0 0 0 0 0 0 ...
 $ X.y.y                  : int  485 486 488 490 491 492 493 494 495 496 ...
 $ Tmax_Robinson          : num  4.54 4.54 4.4 4.62 4.62 ...

Ordisurfs top corr

ordsrfs <- list(length = ncol(env_os))

for (i in seq(ncol(env_os))) {
  os.i <- gg_ordisurf(ord = ord,
                      env.var = env_os[, i],
                      pt.size = 1,
                      # binwidth = 0.05,
                      var.label = names(env_os)[i],
                      gen.text.size = 10,
                      title.text.size = 15,
                      leg.text.size = 10)
  
  ordsrfs[[i]] <- os.i$plot
}


ordsrfs_plt <- ggarrange(plotlist = ordsrfs,
                         nrow = 4,
                         ncol = 5)

ordsrfs_plt

Save some outputs
ggexport(ordsrfs_plt,
          filename = file.path(dataPath,"outputs/Barents_below100_ordisurfs_top_corr.png"),
          width = 2000,
          height = 2500)

Sel env var (manual)

env_os_m <- env[,c("Tmean_Robinson", #top corr
                  "salt_max", #top corr
                  "Smax_Robinson", #comparison to top corr
                  "swDensRob_avs", #top corr
                  "BO22_icecoverltmax_ss",#top corr ax2
                  "BO22_icecovermean_ss",#top corr
                  "BO22_dissoxmean_bdmean",#top corr
                  #"BO22_carbonphytoltmin_bdmean",#top corr - no clear gradient in ordisurf
                  "BO22_ppltmin_ss", #top corr
                  "X.y", #comparison to Y
                  "Y", #top corr
                  "spd_std", #top corr ax2 (blended model)
                  "CSpdsd_Robinson", #comparison to top corr ax2 (blended model)
                  "mud", #highest sed var ax1 + corr
                  "gravel",#highest sed var ax1 - corr
                  "BO22_silicateltmax_bdmean", #just under top corr ax1
                  "bathy" #intuitive for comparisons
                )]
env_os_m
str(env_os_m)
'data.frame':   1141 obs. of  16 variables:
 $ Tmean_Robinson           : num  4.07 4.07 4.06 3.9 3.9 ...
 $ salt_max                 : num  35 35 35 35 35 ...
 $ Smax_Robinson            : num  35.1 35.1 35.1 35.1 35.1 ...
 $ swDensRob_avs            : num  1029 1029 1029 1029 1029 ...
 $ BO22_icecoverltmax_ss    : num  3.86e-06 3.89e-06 3.96e-06 7.71e-06 7.49e-06 ...
 $ BO22_icecovermean_ss     : num  5.82e-07 5.55e-07 5.82e-07 1.06e-06 1.01e-06 ...
 $ BO22_dissoxmean_bdmean   : num  310 310 310 311 311 ...
 $ BO22_ppltmin_ss          : num  0 0 0 0 0 0 0 0 0 0 ...
 $ X.y                      : num  1075461 1075261 1075261 1073461 1073261 ...
 $ Y                        : num  8125134 8125334 8125534 8137734 8137534 ...
 $ spd_std                  : num  0.054 0.0541 0.0543 0.0516 0.0516 ...
 $ CSpdsd_Robinson          : num  0.0222 0.0223 0.0224 0.0163 0.0165 ...
 $ mud                      : num  69.5 60 60 69.5 69.5 69.5 60 60 60 69.5 ...
 $ gravel                   : num  0.5 15 15 0.5 0.5 0.5 15 15 15 0.5 ...
 $ BO22_silicateltmax_bdmean: num  6.55 6.55 6.55 6.58 6.58 ...
 $ bathy                    : num  -295 -292 -292 -270 -270 ...

Ordisurfs manually selected

ordsrfs_m <- list(length = ncol(env_os_m))

for (i in seq(ncol(env_os_m))) {
  os.i_m <- gg_ordisurf(ord = ord,
                      env.var = env_os_m[, i],
                      pt.size = 1,
                      # binwidth = 0.05,
                      var.label = names(env_os_m)[i],
                      gen.text.size = 10,
                      title.text.size = 15,
                      leg.text.size = 10)
  
  ordsrfs_m[[i]] <- os.i_m$plot
}


ordsrfs_plt_m <- ggarrange(plotlist = ordsrfs_m,
                         nrow = 4,
                         ncol = 4)
Warning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite numberWarning: Computation failed in `stat_contour()`:
'from' must be a finite number
ordsrfs_plt_m

Save some outputs
ggexport(ordsrfs_plt_m,
          filename = file.path(dataPath,"outputs/Barents_ordisurfs_man_sel_domean.png"),
          width = 2000,
          height = 2000)

Envfit

## Select if any var should be excluded from envfit (makes less busy to read)
env_os_m_envfit<-env_os_m [,c("Tmean_Robinson", #top corr
                  "salt_max", #top corr
                  "Smax_Robinson", #comparison to top corr
                  "swDensRob_avs", #top corr
                  "BO22_icecoverltmax_ss",#top corr ax2
                  #"BO22_icecovermean_ss",#top corr
                  "BO22_dissoxmean_bdmean",#top corr
                  #"BO22_dissoxltmin_bdmean",#top corr
                  #"BO22_carbonphytoltmin_bdmean",#top corr - no clear gradient in ordisurf
                  "BO22_ppltmin_ss", #top corr
                  "X.y", #comparison to Y
                  "Y", #top corr
                  "spd_std", #top corr ax2 (blended model)
                 # "CSpdsd_Robinson", #comparison to top corr ax2 (blended model)
                  "mud", #highest sed var ax1 + corr
                  "gravel",#highest sed var ax1 - corr
                  "BO22_silicateltmax_bdmean", #just under top corr ax1
                  "bathy" #intuitive for comparisons
                
  
)]

colnames(env_os_m_envfit)<-c("T", #top corr
                  "sMx", #top corr
                  "SmaxR", #comparison to top corr
                  "swDensR", #top corr
                  "icecovmax",#top corr ax2
                  #"icecovav",#top corr
                  "dissoxav",#top corr
                  #"dissoxmin",#top corr
                  #"BO22_carbonphytoltmin_bdmean",#top corr - no clear gradient in ordisurf
                  "ppltmin", #top corr
                  "X", #comparison to Y
                  "Y", #top corr
                  "spd_std", #top corr ax2 (blended model)
                 # "CSsd", #comparison to top corr ax2 (blended model)
                  "mud", #highest sed var ax1 + corr
                  "gravel",#highest sed var ax1 - corr
                  "SiLtmax", #just under top corr ax1
                  "bathy" #intuitive for comparisons
                 )

## Envfot plot
gg_envfit(ord = ord,
          env = env_os_m_envfit,
          pt.size = 1)
Error in envfit.default(ord, env, choices = choices, perm = perm) : 
  missing values in data: consider na.rm = TRUE
Save the plot
ggsave(filename = file.path(dataPath,"outputs/Barents_EnvFit_man_sel_cln_domean.png"),
       device = "png",
       dpi=300 )
Saving 7 x 7 in image

Categorical envVar visualised on the mdsplots

use dataset with categorical var included

SampID <- env_sort$SampID

env_vis<-env
env_vis$gnmds1 <- otu_6$gnmds1
env_vis$gnmds2 <- otu_6$gnmds2

env_vis$dca1 <- otu_6$dca1
env_vis$dca2 <- otu_6$dca2

gnmds w mld mean - bathy

dca_int <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("Interactive DCA sample ID plot",
          subtitle = "First run") +
  geom_point(aes(colour = factor(SampID))) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")

ggplotly(dca_int)
Error in `check_aesthetics()`:
! Aesthetics must be either length 1 or the same as the data (1137): colour
Backtrace:
 1. plotly::ggplotly(dca_int)
 2. plotly:::ggplotly.ggplot(dca_int)
 3. plotly::gg2list(...)
 4. plotly (local) ggplotly_build(p)
 5. plotly (local) by_layer(function(l, d) l$compute_aesthetics(d, plot))
 6. plotly (local) f(l = layers[[i]], d = data[[i]])
 7. l$compute_aesthetics(d, plot)
 8. ggplot2 (local) f(..., self = self)
 9. ggplot2:::check_aesthetics(evaled, n)

gnmds w mld mean - bathy

p_mld <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by proximity to mixed layer depth",
          subtitle = "First run") +
  geom_point(aes(colour = MLDmean_bathy)) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")

p_mld

gnmds w sedclass

deal with sedclass codes
env_vis<- env_vis %>% 
  mutate(
    sedclassName = case_when(
            sedclass == "1" ~ "SedCoverR",
            sedclass == "5" ~ "Rock",
            sedclass == "20" ~ "Mud",
            sedclass == "21" ~ "MwBlock",
            sedclass == "40" ~ "sMud",
            sedclass == "80" ~ "mSand",
            sedclass == "100" ~ "Sand",
            sedclass == "110" ~ "gMud",
            sedclass == "115" ~ "gsMud",
            sedclass == "120" ~ "gmSand",
            sedclass == "130" ~ "gSand",
            sedclass == "150" ~ "MSG",
            sedclass == "160" ~ "sGravel",
            sedclass == "170" ~ "Gravel",
            sedclass == "175" ~ "GravBlock",
            sedclass == "185" ~ "SGBmix",
            sedclass == "205" ~ "S/MwB",
            sedclass == "206" ~ "S/MwG/B",
            sedclass == "215" ~ "SGBalt",
            sedclass == "300" ~ "HardSed",
            sedclass == "500" ~ "Biogenic"
                        
    )
  )
colour palette to cope with up to 25 categorical colours
c25 <- c(
  "dodgerblue2", "#E31A1C", # red
  "green4",
  "#6A3D9A", # purple
  "#FF7F00", # orange
  "black", "gold1",
  "skyblue2", "#FB9A99", # lt pink
  "palegreen2",
  "#CAB2D6", # lt purple
  "#FDBF6F", # lt orange
  "gray70", "khaki2",
  "maroon", "orchid1", "deeppink1", "blue1", "steelblue4",
  "darkturquoise", "green1", "yellow4", "yellow3",
  "darkorange4", "brown"
)

p_sed <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by sediment class",
          subtitle = "First run") +
  geom_point(aes(colour = factor(sedclassName))) +
   scale_colour_manual(values=c25)+
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")+
  guides(colour=guide_legend(ncol=2))

p_sed

gnmds w lanscape

deal with sedclass codes
env_vis<- env_vis %>% 
  mutate(
    landscapeName = case_when(
            landscape == "1" ~ "Strandflat",
            landscape == "21" ~ "ContSlope",
            landscape == "22" ~ "Canyon",
            landscape == "31" ~ "Valley",
            landscape == "32" ~ "Fjord",
            landscape == "41" ~ "DeepPlain",
            landscape == "42" ~ "SlopePlain",
            landscape == "43" ~ "ShelfPlain",
            landscape == "431" ~ "shallowValley"
    )
  )

p_land <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by landscape class",
          subtitle = "First run") +
  geom_point(aes(colour = factor(landscapeName))) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")+
  guides(colour=guide_legend(ncol=2))

p_land

Gnmds w gmorph


p_gmo <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by landscape class",
          subtitle = "First run") +
  geom_point(aes(colour = factor(gmorph))) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")+
  guides(colour=guide_legend(ncol=2))

p_gmo

cat_var_plots<-p_mld+p_sed+p_land+p_gmo
Save the plot
ggexport(cat_var_plots,
          filename = file.path(dataPath,"outputs/Barents_gnmds_catvar.png"),
          width = 1000,
          height = 800)

Sample identification in the mdsplot

p_gmo <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by sample",
          subtitle = "First run") +
  geom_point(aes(colour = factor(SampID))) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")+
  guides(colour="none", size="none")

ggplotly(p_gmo)
Error in `check_aesthetics()`:
! Aesthetics must be either length 1 or the same as the data (1137): colour
Backtrace:
 1. plotly::ggplotly(p_gmo)
 2. plotly:::ggplotly.ggplot(p_gmo)
 3. plotly::gg2list(...)
 4. plotly (local) ggplotly_build(p)
 5. plotly (local) by_layer(function(l, d) l$compute_aesthetics(d, plot))
 6. plotly (local) f(l = layers[[i]], d = data[[i]])
 7. l$compute_aesthetics(d, plot)
 8. ggplot2 (local) f(..., self = self)
 9. ggplot2:::check_aesthetics(evaled, n)
LS0tDQp0aXRsZTogIk1BUkVBTk8gLSBOaU4gLSBsb0RlbnMgPDEwMzEuOCB3aXRob3V0IFNwaXRzYmVyZ2VuQmFua2VuIG91dGxpZXJzIg0KYXV0aG9yczogVGhpanMgdmFuIFNvbiwgUnVuZSBIYWx2b3JzZW4sIFJlYmVjY2EgUm9zcywgR2Vub3ZldmEgR29uemFsZXMtTWlyZWxpcywgTWFyZ2FyZXQgRG9sYW4NCmRhdGU6ICJMYXN0IFJlbmRlcmVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGZpZ193aWR0aDogNw0KICAgIGZpZ19oZWlnaHQ6IDcNCmFsd2F5c19hbGxvd19odG1sOiB0cnVlIA0KLS0tDQoNCiMjIFBhY2thZ2VzDQpgYGB7cn0NCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2djb3JycGxvdCkNCmxpYnJhcnkoZ2dwdWJyKQ0KbGlicmFyeShwYXRjaHdvcmspDQojIGxpYnJhcnkoImhlcmUiKQ0KIyBsaWJyYXJ5KCJib29rZG93biIpDQojIGxpYnJhcnkoImRvd25sb2FkdGhpcyIpDQpsaWJyYXJ5KHZlZ2FuKQ0KbGlicmFyeShwbHlyKQ0KbGlicmFyeShlMTA3MSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh2aXJpZGlzKQ0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KGdncmVwZWwpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KG9jZSkgDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KGZ1cnJyKQ0KDQojaW5zdGFsbF9naXRodWIoImpmcTMvZ2dvcmRpcGxvdHMiKQ0KbGlicmFyeShnZ29yZGlwbG90cykNCg0KDQpzb3VyY2UoImdnX29yZGlzdXJmX3ZpcmlkaXMuUiIpDQpgYGANCg0KTE9BRCBPQkpFQ1RTIElGIEhBVkUgUlVOIEFMUkVBRFkNCmBgYHtyfQ0KZ2VvZGlzdF9wYTwtcmVhZFJEUyhmaWxlLnBhdGgoZGF0YVBhdGgsImlucHV0cy9CYXJlbnRzX2dlb2Rpc3RfcGEucmRzIikpDQpnZW9kaXN0X3I2PC1yZWFkUkRTKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL0JhcmVudHNfZ2VvZGlzdF9yNi5yZHMiKSkNCg0KbWRzX3BhPC1yZWFkUkRTKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL0JhcmVudHNfbWRzX3BhLnJkcyIpKQ0KbWRzX3I2PC1yZWFkUkRTKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL0JhcmVudHNfbWRzX3I2LnJkcyIpKQ0KI21kc19yNl8xMDAwPC1yZWFkUkRTKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL0JhcmVudHNfbWRzX3I2XzEwMDByZXAucmRzIikpDQoNCmVwPC0wLjggI2Vwc2lsb24gKHRvIGF2b2lkIGhhdmluZyB0byBmaW5kIGFuZCBydW4ganVzdCB0aGlzIGxpbmUgZnJvbSBjb2RlIGJsb2NrcyB3aGVyZSB0aGUgbWRzIG9iamVjdHMgd2VyZSBtYWRlKQ0KYGBgDQoNCg0KIyMgRGF0YQ0KYGBge3J9DQoNCmVudl9zb3J0IDwtIHJlYWQuY3N2KGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9CQVJFTlRTL0xEbm9TQmhpMDJfZW52X3NvcnRfMjAyMi0wOS0yOS5jc3YiKSkgJT4lIGFzLmRhdGEuZnJhbWUNCm90dV9zb3J0IDwtIHJlYWQuY3N2KGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9CQVJFTlRTL0xEbm9TQmhpMDJfb3R1X3NvcnRfMjAyMi0wOS0yOS5jc3YiKSkgJT4lIGFzLmRhdGEuZnJhbWUNCg0KDQojbXVzdCBiZSBzYW1lIGxlbmd0aCBhbmQgZXF1YWwgdG8gdW5pcXVlIHNhbXBsZSBudW1iZXIgbGVuZ3RoIC0gdGhlIG9yZGVyZWQgbGlzdHMgYXJlIGFzc3VtZWQgdG8gYmUgZGlyZWN0bHkgcmVsYXRhYmxlIG9uIGEgcm93IGJ5IHJvdyBiYXNpcw0KDQpqb2luZWREYXQ8LSBsZWZ0X2pvaW4oZW52X3NvcnQsb3R1X3NvcnQsIGJ5PWMoIlNhbXBJRCI9IlNhbXBJRCIpKQ0Kam9pbmVkRGF0MTwtc3Vic2V0KGpvaW5lZERhdCwgYmF0aHkgPD0gLTk5KQ0Kc3VtbWFyeShqb2luZWREYXQxJGJhdGh5KQ0KDQplbnZfc29ydDwtam9pbmVkRGF0MSAlPiUgc2VsZWN0KGMoMjozNTEpKQ0Kb3R1X3NvcnQ8LWpvaW5lZERhdDEgJT4lIHNlbGVjdChjKDIsMzUyOjQ5MSkpDQoNCiN0YWJsZShlbnZfc29ydCRTYW1wSUQyID09IG90dV9zb3J0JFNhbXBJRCkNCmRpbShlbnZfc29ydCkNCmRpbShvdHVfc29ydCkNCmBgYA0KDQojIyMjIHJlbW92ZSBhbnkgdmFyaWFibGVzIHdpdGggbm90IGVub3VnaCBjb3ZlcmFnZQ0KYGBge3J9DQoNCmVudl9zb3J0IDwtIGVudl9zb3J0ICU+JSBzZWxlY3QoLWMoIkJPMjJfbGlnaHRib3RtZWFuX2JkbWVhbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJPMjJfbGlnaHRib3RsdG1heF9iZG1lYW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCTzIyX2xpZ2h0Ym90bHRtaW5fYmRtZWFuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQk8yMl9saWdodGJvdHJhbmdlX2JkbWVhbiIpKQ0KYGBgDQoNCg0KIyMjIyBQbG90IG1hcCB0byBjaGVjayBsb2NhdGlvbiBvZiBzYW1wbGVzDQpgYGB7cn0NCmVudl9zb3J0X2xvY2F0aW9uczwtIGdncGxvdChkYXRhID0gZW52X3NvcnQsDQogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBYLnksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBZKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBiYXRoeSksDQogICAgICAgICAgICAgc2l6ZSA9IDIpICsNCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAicmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtaWQgPSAieWVsbG93IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gImdyZWVuIikgKw0KICBnZ3RpdGxlKCJMb2NhdGlvbiBvZiBzYW1wbGVzIGluIHNvcnRlZCBlbnYgZmlsZSIpDQoNCmVudl9zb3J0X2xvY2F0aW9ucw0KYGBgDQoNCg0KIyMjIERhdGEgY2xlYW5pbmcNCmBgYHtyfQ0KIyMgUmVtb3ZpbmcgTkFzDQpvdHVDb21wbCA8LSBvdHVfc29ydFtjb21wbGV0ZS5jYXNlcyhlbnZfc29ydFssIC1jKDE6d2hpY2goY29sbmFtZXMoZW52X3NvcnQpPT0iYmF0aHkiKS0xKV0pLCBdICMgbWFrZSBzdXJlIHdlIGhhdmUgdGhlIGNvbHVtbXMgDQplbnZDb21wbCA8LSBlbnZfc29ydFtjb21wbGV0ZS5jYXNlcyhlbnZfc29ydFssIC1jKDE6d2hpY2goY29sbmFtZXMoZW52X3NvcnQpPT0iYmF0aHkiKS0xKV0pLCBdDQoNCiMjIFJlbW92aW5nIG9ic2VydmF0aW9ucyB3aXRoIGxlc3MgdGhhbiA0IE9UVXMNCnNlbCA8LSByb3dTdW1zKG90dUNvbXBsWywgLWMoMToyKV0pID49IDQNCm90dVNlbCA8LSBvdHVDb21wbFtzZWwsIF0NCmVudlNlbCA8LSBlbnZDb21wbFtzZWwsIF0NCg0KDQojIyBSZW1vdmluZyBwaG9zcGhhdGUNCiNlbnZTZWwgPC0gZW52U2VsICU+JSBzZWxlY3QoLXBob3NwaGF0ZV9tZWFuLnRpZikNCg0KDQpkaW0ob3R1U2VsKTsgZGltKGVudlNlbCkNCmBgYA0KIyMjIyBTaG93IHdoYXQgc2FtcGxlcyBhcmUgbGVmdCBhZnRlciBjb21wbGV0ZSBjYXNlcyBhbmQgPjQgT1RVcyBmaWx0ZXJzDQpgYGB7cn0NCmVudl9zZWxfbG9jYXRpb25zPC0gZ2dwbG90KGRhdGEgPSBlbnZTZWwsDQogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBYLnksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBZKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBiYXRoeSksDQogICAgICAgICAgICAgc2l6ZSA9IDIpICsNCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAicmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtaWQgPSAieWVsbG93IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gImdyZWVuIikgKw0KICBnZ3RpdGxlKCJMb2NhdGlvbiBvZiBzYW1wbGVzIGluIHNvcnRlZCBlbnYgZmlsZSIpDQoNCmVudl9zZWxfbG9jYXRpb25zDQpgYGANCg0KIyMjIyBTaG93IHdoYXQgZ290IHJlbW92ZWQgaW4gY29tcGxldGUgY2FzZXMgZmlsdGVyDQpgYGB7cn0NCmVudkNvbXBsX2NjcmVtPC1lbnZfc29ydCU+JWZpbHRlcighU2FtcElEJWluJWVudkNvbXBsJFNhbXBJRCkNCg0KZW52Q29tcGxfY2NyZW1fbG9jYXRpb25zPC0gZ2dwbG90KGRhdGEgPSBlbnZDb21wbF9jY3JlbSwNCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFgueSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFkpKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGJhdGh5KSwNCiAgICAgICAgICAgICBzaXplID0gMikgKw0KICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKGxvdyA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pZCA9ICJ5ZWxsb3ciLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiZ3JlZW4iKSArDQogIGdndGl0bGUoIkxvY2F0aW9uIG9mIHJlbW92ZWQgY29tcGxldGUgY2FzZSBzYW1wbGVzIHJlc3VsdGluZyBpbiBlbnZTZWwgZmlsZSIpDQoNCmVudkNvbXBsX2NjcmVtX2xvY2F0aW9ucw0KYGBgDQojIyMjIFNob3cgd2hhdCBnb3QgcmVtb3ZlZCBpbiA8NCBPVFVzIGZpbHRlcg0KDQoNCmBgYHtyfQ0KaW52LnNlbCA8LSByb3dTdW1zKG90dUNvbXBsWywgLWMoMToyKV0pIDwgNA0KZW52LmludlNlbCA8LSBlbnZDb21wbFtpbnYuc2VsLCBdDQoNCmVudi5pbnZTZWxfbG9jYXRpb25zPC0gZ2dwbG90KGRhdGEgPSBlbnYuaW52U2VsLA0KICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gWC55LA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gWSkpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gYmF0aHkpLA0KICAgICAgICAgICAgIHNpemUgPSAyKSArDQogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIobG93ID0gInJlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gInllbGxvdyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJncmVlbiIpICsNCiAgZ2d0aXRsZSgiTG9jYXRpb24gb2YgcmVtb3ZlZCBzYW1wbGVzIHdpdGggPDQgT1RVcyByZXN1bHRpbmcgaW4gZW52U2VsIGZpbGUiKQ0KDQplbnYuaW52U2VsX2xvY2F0aW9ucw0KDQpgYGANCg0KIyMjIFtPcHRpb25hbF0gRGF0YSB0aGlubmluZw0KYGBge3IsIGV2YWw9RkFMU0V9DQojIG90dV9yZWQgPC0gb3R1MVsxOjUwMCwgXQ0KIyBvdHUgPC0gb3R1X3JlZA0KIyANCiMgZW52X3JlZCA8LSBlbnZbMTo1MDAsIF0NCiMgZW52IDwtIGVudl9yZWQNCmBgYA0KDQojIyMgV2hvbGUgZGF0YXNldCBmb3IgZmlyc3QgcnVuDQpgYGB7cn0NCg0Kb3R1PC1vdHVTZWxbLC1jKDE6IHdoaWNoKGNvbG5hbWVzKG90dVNlbCk9PSJMaXRob2RpZGFlIiktMSldDQplbnY8LWVudlNlbFssLWMoMTogd2hpY2goY29sbmFtZXMoZW52X3NvcnQpPT0iYmF0aHkiKS0xKV0NCg0KdGFibGUoaXMubmEob3R1KSkNCg0KdGFibGUoaXMubmEoZW52KSkNCg0KI3N0cihvdHVTZWwpDQojc3RyKGVudlNlbCkNCmBgYA0KDQoNCiMjIyBTcGxpdHRpbmcgaW4gc3Vic2V0cw0KYGBge3J9DQojICMjIENsYXNzIDENCiMgb3R1MSA8LSBzdWJzZXQob3R1U2VsLCBlbnZTZWwkU3BsaXRSZXYgPT0gMSkNCiMgZW52MSA8LSBlbnZTZWwgJT4lIGZpbHRlcihTcGxpdFJldiA9PSAxKQ0KIyANCiMgIyMgQ2xhc3MgMg0KIyBvdHUyIDwtIHN1YnNldChvdHVTZWwsIGVudlNlbCRTcGxpdFJldiA9PSAyKQ0KIyBlbnYyIDwtIGVudlNlbCAlPiUgZmlsdGVyKFNwbGl0UmV2ID09IDIpDQojIA0KIyAjIyBDbGFzcyA0DQojIG90dTMgPC0gc3Vic2V0KG90dVNlbCwgZW52U2VsJFNwbGl0UmV2ID09IDMpDQojIGVudjMgPC0gZW52U2VsICU+JSBmaWx0ZXIoU3BsaXRSZXYgPT0gMykNCiMgDQojICMjIENsYXNzIDQNCiMgb3R1NCA8LSBzdWJzZXQob3R1U2VsLCBlbnZTZWwkU3BsaXRSZXYgPT0gNCkNCiMgZW52NCA8LSBlbnZTZWwgJT4lIGZpbHRlcihTcGxpdFJldiA9PSA0KQ0KIyANCiMgIyMgQ2xhc3MgNg0KIyBvdHU2IDwtIHN1YnNldChvdHVTZWwsIGVudlNlbCRTcGxpdFJldiA9PSA2KQ0KIyBlbnY2IDwtIGVudlNlbCAlPiUgZmlsdGVyKFNwbGl0UmV2ID09IDYpDQojIA0KIyAjIyBDbGFzcyA3DQojIG90dTcgPC0gc3Vic2V0KG90dVNlbCwgZW52U2VsJFNwbGl0UmV2ID09IDcpDQojIGVudjcgPC0gZW52U2VsICU+JSBmaWx0ZXIoU3BsaXRSZXYgPT0gNykNCiMgDQojICMjIENsYXNzIDgNCiMgb3R1OCA8LSBzdWJzZXQob3R1U2VsLCBlbnZTZWwkU3BsaXRSZXYgPT0gOCkNCiMgZW52OCA8LSBlbnZTZWwgJT4lIGZpbHRlcihTcGxpdFJldiA9PSA4KQ0KYGBgDQoNCiMjIFNlbGVjdGluZyBzdWJzZXQNCmBgYHtyfQ0KIyAjIyBTZWxlY3RpbmcgDQojIG90dSA8LSBvdHUyDQojIGVudiA8LSBlbnYyDQoNCmBgYA0KDQojIyBmb3JtYXQgZGF0YSBjb3JyZWN0bHkNCmBgYHtyfQ0KI2VudiRjb29yZHMueDE8LWFzLm51bWVyaWMoZW52JGNvb3Jkcy54MSkNCiNlbnYkY29vcmRzLngyPC1hcy5udW1lcmljKGVudiRjb29yZHMueDIpDQoNCg0KYGBgDQoNCg0KIyMgQWJ1bmRhbmNlIHdlaWdodGluZw0KTWFrZSBmdW5jdGlvbg0KWW91IG1pZ2h0IGhhdmUgdG8gZHJvcCB2YXJpYWJsZXMgdGhhdCBoYXZlIGJlZW4gaW1wb3J0ZWQgYXMgY2hhcmFjdGVyDQpgYGB7cn0NCg0Kb3R1X3BhIDwtIGRlY29zdGFuZCh4ID0gb3R1WywgLWMoMSldLA0KICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicGEiKQ0KDQojIHkgPSBheF53ICAgICAgICAgIyBwb3dlciB0cmFuc2Zvcm1hdGlvbiBmb3JtdWxhDQpkdCA8LSBvdHVbLCAtYygxOjIpXSAgICAgICAgICAjIHNwZWNpZXMgZGF0YSB0byB0cmFuc2Zvcm0NCnhfbW4gPC0gbWluKGR0W2R0ID4gMF0pDQp4X214IDwtIG1heChkdCkNCnJuZyA8LSA2ICAgICAgICAgICAjIGFidW5kYW5jZSByYW5nZQ0KdyA8LSBsb2cocm5nKSAvIChsb2coeF9teCkgLSBsb2coeF9tbikpDQphIDwtIHhfbW5eKC13KQ0KIyBvdHVfNiA8LSBhICogZHRbLCAtYygxOjMpXV53DQpvdHVfNiA8LSBhICogZHRedw0KcmFuZ2Uob3R1XzYpDQpgYGANCg0KIyMgT3JkaW5hdGlvbiBtZXRob2RzDQojIyMgRENBIFBBDQpgYGB7cn0NClN5cy50aW1lKCkNCmRjYV9wYSA8LSBkZWNvcmFuYSh2ZWcgPSBvdHVfcGEpDQoNCnByaW50KGRjYV9wYSwgaGVhZD1UKQ0KYGBgDQoNCg0KIyMjIEdOTURTIFBBDQoNCiMjIyMgRGlzdGFuY2VzIC0gZG9uJ3QgcnVuIGlmIGxvYWRpbmcgc2F2ZWQgb2JqZWN0DQoNCmBgYHtyfQ0KIyMgQnJheS1DdXJ0aXMNCmRpc3RfcGEgPC0gdmVnZGlzdCh4ID0gb3R1X3BhLCBtZXRob2QgPSAiYnJheSIpDQoNCiMjIEdlb2Rpc3QNCmVwIDwtIDAuOCAgICAgIyBlcHNpbG9uDQpnZW9kaXN0X3BhIDwtIGlzb21hcGRpc3QoZGlzdCA9IGRpc3RfcGEsIGVwc2lsb24gPSBlcCkNCmBgYA0KDQoNCiMjIyMjIFNhdmUgdGhlIHJlc3VsdCAtIGRvbid0IHJ1biBpZiBsb2FkaW5nIHNhdmVkIG9iamVjdA0KYGBge3J9DQpzYXZlUkRTKGdlb2Rpc3RfcGEsDQogICAgICAgIGZpbGUgPSAoZmlsZS5wYXRoKGRhdGFQYXRoLCJpbnB1dHMvQmFyZW50c19nZW9kaXN0X3BhLnJkcyIpKSkNCmBgYA0KDQojIyMjIE9yZGluYXRpb24NCg0KIyMjIyBtb25vTURTIC0gZG9uJ3QgcnVuIGlmIGxvYWRpbmcgc2F2ZWQgb2JqZWN0DQp0b29rIDYuNG1pbnMgd2l0aCBqb25hdGFuJ3MgcGFyYWxlbGwgc29sdXRpb24gKHVzdWFsbHkgfjEwbWlucykNCmBgYHtyfQ0KIyANCiMgU3lzLnRpbWUoKQ0KIyBkIDwtIDINCiMgbWRzX3BhIDwtIGxpc3QoKQ0KIyANCiMgZm9yIChpIGluIDE6MTAwKSB7DQojICAgbWRzX3BhW1tpXV08LW1vbm9NRFMoZ2VvZGlzdF9wYSwNCiMgICAgICAgICAgICAgICAgICAgICBtYXRyaXgoYyhydW5pZihkaW0ob3R1X3BhKVsxXSpkKSksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSBkaW0ob3R1X3BhKVsxXSksDQojICAgICAgICAgICAgICAgICAgICAgayA9IGQsDQojICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiZ2xvYmFsIiwNCiMgICAgICAgICAgICAgICAgICAgICBtYXhpdCA9IDIwMDAsDQojICAgICAgICAgICAgICAgICAgICAgc21pbiA9IDFlLTcsDQojICAgICAgICAgICAgICAgICAgICAgc2Zncm1pbiA9IDFlLTcpDQojIH0NCiMgU3lzLnRpbWUoKQ0KDQoNCiMjIG1vbm9NRFMNCiMgZCA8LSAyDQojIG1kc19yNiA8LSBsaXN0KCkNCiMgDQojIFN5cy50aW1lKCkNCiMgZm9yIChpIGluIDE6MjAwKSB7DQojICAgbWRzX3I2W1tpXV08LW1vbm9NRFMoZ2VvZGlzdF9yNiwNCiMgICAgICAgICAgICAgICAgICAgICBtYXRyaXgoYyhydW5pZihkaW0ob3R1XzYpWzFdKmQpKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IGRpbShvdHVfNilbMV0pLA0KIyAgICAgICAgICAgICAgICAgICAgIGsgPSBkLA0KIyAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gImdsb2JhbCIsDQojICAgICAgICAgICAgICAgICAgICAgbWF4aXQgPSAyMDAwLA0KIyAgICAgICAgICAgICAgICAgICAgIHNtaW4gPSAxZS03LA0KIyAgICAgICAgICAgICAgICAgICAgIHNmZ3JtaW4gPSAxZS03KQ0KIyB9DQojIFN5cy50aW1lKCkNCg0KI0pvbmF0YW4ncyB1cGdyYWRlIGZvciBzcGVlZCAtIHBhcmFsbGVsIHByb2Nlc3Npbmcgb2YgbWRzIHJlcHRpdGlvbnMNCmxpYnJhcnkoZnVycnIpICMgUGFja2FnZSB0byBydW4gbm9uIHNlcXVlbnRpYWwgZnVuY3Rpb25zIGluIHBhcmFsbGVsDQojbGlicmFyeShwdXJycikNCnBsYW4obXVsdGlzZXNzaW9uKQ0KDQpkIDwtIDINCg0KaSA8LTIwMCAjIG51bWJlciBvZiByZXBzDQoNCkxpc3RfZ2VvZGlzdF9wYSA8LSBsYXBwbHkoc2VxX2xlbihpKSwgZnVuY3Rpb24oWCkgZ2VvZGlzdF9wYSkgIyBtYWtlcyBhIGxpc3Qgd2l0aCB0aGUgaW5wdXQgZGF0YSByZXBlYXRlZCBhcyBtYW55IHRpbWVzIGFzIHJlcHMgd2FudGVkDQpzdGFydF90PC1TeXMudGltZSgpDQpYbWRzX3BhPC1mdXJycjo6ZnV0dXJlX21hcChMaXN0X2dlb2Rpc3RfcGEsIA0KICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgbW9ub01EUyh4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXRyaXgoYyhydW5pZihkaW0ob3R1XzYpWzFdKmQpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSBkaW0ob3R1XzYpWzFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgayA9IGQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gImdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heGl0ID0gMjAwMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc21pbiA9IDFlLTcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNmZ3JtaW4gPSAxZS03KSwNCiAgICAgICAgICAgICAgICAgIC5wcm9ncmVzcyA9IFRSVUUpDQpTeXMudGltZSgpIC0gc3RhcnRfdA0KDQoNCg0KYGBgDQoNCiMjIyMjIFNhdmUgdGhlIHJlc3VsdCAtIGRvbid0IHJ1biBpZiBsb2FkaW5nIHNhdmVkIG9iamVjdA0KY2FuIHRha2UgYSBmZXcgbWlucw0KYGBge3J9DQpzYXZlUkRTKFhtZHNfcGEsDQogICAgICAgIGZpbGUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsImlucHV0cy9CYXJlbnRzX21kc19iYXJlbnRzX3BhLnJkcyIpKQ0KYGBgDQoNCg0KIyMjIyMgQmVzdCBubWRzIHNvbHV0aW9uIC0gUEENCk1ha2UgZnVuY3Rpb24/DQpgYGB7ciwgZXZhbD1UUlVFfQ0KIyBMb2FkaW5nIGdlb2Rpc3Qgb2JqZWN0DQojIGdlb2Rpc3RfbmZpIDwtIHJlYWRSRFMoZmlsZSA9ICIuLi9TcGxpdFJldjJfZ2VvZGlzdF9wYV9mdWxsLnJkcyIpDQpgYGANCg0KDQpgYGB7cn0NCiMgTG9hZGluZyBtZHMgcmVzdWx0cw0KIyBtZHMgPC0gcmVhZFJEUyhmaWxlID0gIi4uL1NwbGl0UmV2Ml9tZHNfcGFfZnVsbC5yZHMiKQ0KDQojIyBFeHRyYWN0aW5nIHRoZSBzdHJlc3Mgb2YgZWFjaCBubWRzIGl0ZXJhdGlvbg0KbWRzX3N0cmVzc19wYTwtdW5saXN0KGxhcHBseShYbWRzX3BhLCBmdW5jdGlvbih2KXt2W1syMl1dfSkpIA0KDQpvcmRlcmVkX3BhIDwtb3JkZXIobWRzX3N0cmVzc19wYSkNCg0KIyMgQmVzdCwgc2Vjb25kIGJlc3QsIGFuZCB3b3JzdCBzb2x1dGlvbg0KbWRzX3N0cmVzc19wYVtvcmRlcmVkX3BhWzFdXQ0KbWRzX3N0cmVzc19wYVtvcmRlcmVkX3BhWzJdXQ0KbWRzX3N0cmVzc19wYVtvcmRlcmVkX3BhWzEwXV0NCg0KIyMgU2NhbGluZyBvZiBheGVzIHRvIGhhbGYgY2hhbmdlIHVuaXRzIGFuZCB2YXJpbWF4IHJvdGF0aW9uIGJ5IHBvc3RNRFMNCm1kc19iZXN0X3BhPC1wb3N0TURTKFhtZHNfcGFbW29yZGVyZWRfcGFbMV1dXSwNCiAgICAgICAgICAgICAgICAgIGdlb2Rpc3RfcGEsIA0KICAgICAgICAgICAgICAgICAgcGMgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgIGhhbGZjaGFuZ2UgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgIHRocmVzaG9sZCA9IGVwKSAgICAgIyBJcyB0aGlzIHRocmVzaG9sZCByZWxhdGVkIHRvIHRoZSBlcHNpbG9uIGFib3ZlPw0KbWRzX2Jlc3RfcGENCg0KbWRzX3NlY2Jlc3RfcGEgPC0gcG9zdE1EUyhYbWRzX3BhW1tvcmRlcmVkX3BhWzJdXV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgIGdlb2Rpc3RfcGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBoYWxmY2hhbmdlID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRocmVzaG9sZCA9IGVwKQ0KbWRzX3NlY2Jlc3RfcGENCg0KIyMgUHJvY3J1c3RlcyBjb21wYXJpc29ucw0KcHJvY3JfcGEgPC0gcHJvY3J1c3RlcyhtZHNfYmVzdF9wYSwNCiAgICAgICAgICAgICAgICAgICAgbWRzX3NlY2Jlc3RfcGEsDQogICAgICAgICAgICAgICAgICAgIHBlcm11dGF0aW9ucz05OTkpDQpwcm90ZXN0KG1kc19iZXN0X3BhLA0KICAgICAgICBtZHNfc2VjYmVzdF9wYSwNCiAgICAgICAgcGVybXV0YXRpb25zPTk5OSkNCg0KcGxvdChwcm9jcl9wYSkNCg0KcG5nKGZpbGU9ZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL0JhcmVudHNfcHJvY3J1c3Rlc19wYS5wbmciKSwgd2lkdGg9MTAwMCwgaGVpZ2h0PTcwMCkNCnBsb3QocHJvY3JfcGEpDQpkZXYub2ZmKCkNCmBgYA0KDQojIyMjIyBDb3JyZWxhdGlvbiBvZiBheGlzOiBEQ0EgdnMgTk1EUyAtIFBBDQpgYGB7cn0NCiMgRXh0cmFjdGluZyBvcmRpbmF0aW9uIGF4aXMNCmF4IDwtIDINCmF4aXNfcGEgPC0gY2JpbmQobWRzX2Jlc3RfcGEkcG9pbnRzLA0KICAgICAgICAgICAgICAgICBzY29yZXMoZGNhX3BhLA0KICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheSA9ICJzaXRlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICBvcmlnaW4gPSBUUlVFKVssIDE6YXhdKQ0KDQpnZ2NvcnIoYXhpc19wYSwgDQogICAgICAgbWV0aG9kPWMoImV2ZXJ5dGhpbmciLCJrZW5kYWxsIiksIA0KICAgICAgIGxhYmVsID0gVFJVRSwNCiAgICAgICBsYWJlbF9zaXplID0gMywgDQogICAgICAgbGFiZWxfY29sb3IgPSAiYmxhY2siLCAgDQogICAgICAgbmJyZWFrcyA9IDgsDQogICAgICAgbGFiZWxfcm91bmQgPSAzLA0KICAgICAgIGxvdyA9ICJyZWQiLA0KICAgICAgIG1pZCA9ICJ3aGl0ZSIsDQogICAgICAgaGlnaCA9ICJncmVlbiIpDQoNCg0KYGBgDQoNCiMjIyMjIFNhdmUgdGhlIGZpZ3VyZQ0KYGBge3J9DQpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsIm91dHB1dHMvQmFyZW50c19jb3JyZWxhdGlvblBDQXZzTk1EU19QQS5wbmciKSwNCiAgICAgICBkZXZpY2UgPSAicG5nIiwNCiAgICAgICBkcGk9MzAwICkNCg0KIyBTd2l0Y2hpbmcgZGlyZWN0aW9uIG9mIE5NRFMxDQojIG1kc19iZXN0JHBvaW50c1ssIDFdIDwtIC1tZHNfYmVzdCRwb2ludHNbLCAxXQ0KYGBgDQoNCiMjIyBEQ0EgUjYNCmBgYHtyfQ0KZGNhX3I2IDwtIGRlY29yYW5hKHZlZyA9IG90dV82KQ0KDQpwcmludChkY2FfcjYsIGhlYWQ9VCkNCmBgYA0KDQoNCiMjIyBHTk1EUyBSNg0KIyMjIyBEaXN0YW5jZXMgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QNCmBgYHtyfQ0KIyMgQnJheS1DdXJ0aXMNCmRpc3RfcjYgPC0gdmVnZGlzdCh4ID0gb3R1XzYsIG1ldGhvZCA9ICJicmF5IikNCg0KIyMgR2VvZGlzdA0KZXAgPC0gMC44MCAgICAgIyBlcHNpbG9uDQpnZW9kaXN0X3I2IDwtIGlzb21hcGRpc3QoZGlzdCA9IGRpc3RfcjYsIGVwc2lsb24gPSBlcCkNCmBgYA0KDQojIyMjIyBTYXZlIHRoZSByZXN1bHQgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QNCmBgYHtyfQ0Kc2F2ZVJEUyhnZW9kaXN0X3I2LA0KICAgICAgICBmaWxlID0gKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL0JhcmVudHNfYmVsb3cxMDBfZ2VvZGlzdF9yNi5yZHMiKSkpDQpgYGANCg0KDQojIyMjIE9yZGluYXRpb24gDQoNCiMjIyMjIDIwMCByZXBzIC0gZG9uJ3QgcnVuIGlmIGxvYWRpbmcgc2F2ZWQgb2JqZWN0DQpKb25hdGFuJ3MgcGFyYWxsZWwgc29sdXRpb24gdG9vayA1LjRtaW5zLCBiZWZvcmUgaXQgdG9vayAxMG1pbnMNCg0KDQpgYGB7cn0NCiMjIG1vbm9NRFMNCiMgZCA8LSAyDQojIG1kc19yNiA8LSBsaXN0KCkNCiMgDQojIFN5cy50aW1lKCkNCiMgZm9yIChpIGluIDE6MjAwKSB7DQojICAgbWRzX3I2W1tpXV08LW1vbm9NRFMoZ2VvZGlzdF9yNiwNCiMgICAgICAgICAgICAgICAgICAgICBtYXRyaXgoYyhydW5pZihkaW0ob3R1XzYpWzFdKmQpKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IGRpbShvdHVfNilbMV0pLA0KIyAgICAgICAgICAgICAgICAgICAgIGsgPSBkLA0KIyAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gImdsb2JhbCIsDQojICAgICAgICAgICAgICAgICAgICAgbWF4aXQgPSAyMDAwLA0KIyAgICAgICAgICAgICAgICAgICAgIHNtaW4gPSAxZS03LA0KIyAgICAgICAgICAgICAgICAgICAgIHNmZ3JtaW4gPSAxZS03KQ0KIyB9DQojIFN5cy50aW1lKCkNCg0KI0pvbmF0YW4ncyB1cGdyYWRlIGZvciBzcGVlZCAtIHBhcmFsbGVsIHByb2Nlc3Npbmcgb2YgbWRzIHJlcHRpdGlvbnMNCiNsaWJyYXJ5KGZ1cnJyKSAjIFBhY2thZ2UgdG8gcnVuIG5vbiBzZXF1ZW50aWFsIGZ1bmN0aW9ucyBpbiBwYXJhbGxlbA0KI2xpYnJhcnkocHVycnIpDQpwbGFuKG11bHRpc2Vzc2lvbikNCg0KZCA8LSAyDQoNCmkgPC0yMDAgIyBudW1iZXIgb2YgcmVwcw0KDQpMaXN0X2dlb2Rpc3RfcjYgPC0gbGFwcGx5KHNlcV9sZW4oaSksIGZ1bmN0aW9uKFgpIGdlb2Rpc3RfcjYpICMgbWFrZXMgYSBsaXN0IHdpdGggdGhlIGlucHV0IGRhdGEgcmVwZWF0ZWQgYXMgbWFueSB0aW1lcyBhcyByZXBzIHdhbnRlZA0Kc3RhcnRfdDwtU3lzLnRpbWUoKQ0KWG1kc19yNjwtZnVycnI6OmZ1dHVyZV9tYXAoTGlzdF9nZW9kaXN0X3I2LCANCiAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIG1vbm9NRFMoeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF0cml4KGMocnVuaWYoZGltKG90dV82KVsxXSpkKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gZGltKG90dV82KVsxXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGsgPSBkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9ICJnbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhpdCA9IDIwMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNtaW4gPSAxZS03LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZmdybWluID0gMWUtNyksDQogICAgICAgICAgICAgICAgICAucHJvZ3Jlc3MgPSBUUlVFKQ0KU3lzLnRpbWUoKSAtIHN0YXJ0X3QNCg0KYGBgDQoNCiMjIyMjIFNhdmUgdGhlIHJlc3VsdCAtIGRvbid0IHJ1biBpZiBsb2FkaW5nIHNhdmVkIG9iamVjdA0KYGBge3J9DQpzYXZlUkRTKFhtZHNfcjYsDQogICAgICAgIGZpbGUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsImlucHV0cy9CYXJlbnRzX2JlbG93MTAwX21kc19yNi5yZHMiKSkgDQpgYGANCg0KIyMjIyMgQmVzdCBubWRzIHNvbHV0aW9uIHI2IDIwMCByZXAgDQpgYGB7ciwgZXZhbD1UUlVFfQ0KIyBMb2FkaW5nIGdlb2Rpc3Qgb2JqZWN0DQojIGdlb2Rpc3RfbmZpIDwtIHJlYWRSRFMoZmlsZSA9ICIuLi9TcGxpdFJldjJfZ2VvZGlzdF9uZmkucmRzIikNCg0KIyBMb2FkaW5nIG1kcyByZXN1bHRzDQojIG1kcyA8LSByZWFkUkRTKGZpbGUgPSAiLi4vU3BsaXRSZXYyX21kcy5yZHMiKQ0KDQojIyBFeHRyYWN0aW5nIHRoZSBzdHJlc3Mgb2YgZWFjaCBubWRzIGl0ZXJhdGlvbg0KbWRzX3N0cmVzc19yNjwtdW5saXN0KGxhcHBseShYbWRzX3I2LCBmdW5jdGlvbih2KXt2W1syMl1dfSkpIA0KDQpvcmRlcmVkX3I2IDwtb3JkZXIobWRzX3N0cmVzc19yNikNCg0KIyMgQmVzdCwgc2Vjb25kIGJlc3QsIGFuZCB3b3JzdCBzb2x1dGlvbg0KbWRzX3N0cmVzc19yNltvcmRlcmVkX3I2WzFdXQ0KbWRzX3N0cmVzc19yNltvcmRlcmVkX3I2WzJdXQ0KbWRzX3N0cmVzc19yNltvcmRlcmVkX3I2WzEwMF1dDQoNCiMjIFNjYWxpbmcgb2YgYXhlcyB0byBoYWxmIGNoYW5nZSB1bml0cyBhbmQgdmFyaW1heCByb3RhdGlvbiBieSBwb3N0TURTDQptZHNfYmVzdF9yNjwtcG9zdE1EUyhYbWRzX3I2W1tvcmRlcmVkX3I2WzFdXV0sDQogICAgICAgICAgICAgICAgICAgICBnZW9kaXN0X3I2LCANCiAgICAgICAgICAgICAgICAgICAgIHBjID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICBoYWxmY2hhbmdlID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICB0aHJlc2hvbGQgPSBlcCkgICAgICMgSXMgdGhpcyB0aHJlc2hvbGQgcmVsYXRlZCB0byB0aGUgZXBzaWxvbiBhYm92ZT8NCm1kc19iZXN0X3I2DQoNCm1kc19zZWNiZXN0X3I2PC1wb3N0TURTKFhtZHNfcjZbW29yZGVyZWRfcjZbMl1dXSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGdlb2Rpc3RfcjYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgcGMgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGhhbGZjaGFuZ2UgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHRocmVzaG9sZCA9IGVwKQ0KbWRzX3NlY2Jlc3RfcjYNCg0KIyMgUHJvY3J1c3RlcyBjb21wYXJpc29ucw0KcHJvY3JfcjYgPC0gcHJvY3J1c3RlcyhtZHNfYmVzdF9yNiwNCiAgICAgICAgICAgICAgICAgICAgICAgbWRzX3NlY2Jlc3RfcjYsDQogICAgICAgICAgICAgICAgICAgICAgIHBlcm11dGF0aW9ucz05OTkpDQpwcm90ZXN0KG1kc19iZXN0X3I2LA0KICAgICAgICBtZHNfc2VjYmVzdF9yNiwNCiAgICAgICAgcGVybXV0YXRpb25zPTk5OSkNCg0KcGxvdChwcm9jcl9yNikNCg0KcG5nKGZpbGUucGF0aChkYXRhUGF0aCwib3V0cHV0cy9CYXJlbnRzX2JlbG93MTAwX3Byb2NydXN0ZXNfcjYucG5nIiksIHdpZHRoPTEwMDAsIGhlaWdodD03MDAsKSAjYWRkZWQgMTAwMA0KcGxvdChwcm9jcl9yNikNCmRldi5vZmYoKQ0KYGBgDQoNCiAjIyMjIDEwMDAgcmVwcyAtIGRvbid0IHJ1biBpZiBsb2FkaW5nIHNhdmVkIG9iamVjdCANCiBDdXJyZW50bHkgY29tbWVudGVkIG91dCBhcyBpdCBnYWluZWQgbm90aGluZyBidXQgdG9vayBleHRyYSB0aW1lLiBDYW4gYmUgcmVtb3ZlZCBpbiBkdWUgY291cnNlLg0KPCEtLSB0b29rIDFociAtLT4NCjwhLS0gYGBge3J9IC0tPg0KPCEtLSAjIG1vbm9NRFMgLS0+DQo8IS0tIGQgPC0gMiAtLT4NCjwhLS0gbWRzX3I2XzEwMDAgPC0gbGlzdCgpI29iaiBhZGRlZF8xMDAwIC0tPg0KDQo8IS0tIFN5cy50aW1lKCkgLS0+DQo8IS0tIGZvciAoaSBpbiAxOjEwMDApIHsjY2hhbmdlIGZyb20gMjAwICgyMDAgcmEgaW4gMjBtaW4sIDEwMDAgcmFuIGluIDFocikgLS0+DQo8IS0tICAgbWRzX3I2XzEwMDBbW2ldXTwtbW9ub01EUyhnZW9kaXN0X3I2LCNvYmogYWRkZWQgXzEwMDAgLS0+DQo8IS0tICAgICAgICAgICAgICAgICAgICAgbWF0cml4KGMocnVuaWYoZGltKG90dV82KVsxXSpkKSksIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gZGltKG90dV82KVsxXSksIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgIGsgPSBkLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICBtb2RlbCA9ICJnbG9iYWwiLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICBtYXhpdCA9IDIwMDAsIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgIHNtaW4gPSAxZS03LCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICBzZmdybWluID0gMWUtNykgLS0+DQo8IS0tIH0gLS0+DQo8IS0tIFN5cy50aW1lKCkgLS0+DQo8IS0tIGBgYCAtLT4NCg0KPCEtLSAjIyMjIyBTYXZlIHRoZSByZXN1bHQgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QgLS0+DQo8IS0tIGBgYHtyfSAtLT4NCjwhLS0gc2F2ZVJEUyhtZHNfcjZfMTAwMCwgI29iaiBhZGRlZF8xMDAwIC0tPg0KPCEtLSAgICAgICAgIGZpbGUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsImlucHV0cy9tZHNfcjZfMTAwMHJlcC5yZHMiKSkgI2NoYW5nZSBmcm9tIDIwMCAtLT4NCjwhLS0gYGBgIC0tPg0KDQo8IS0tICMjIyMjIEJlc3Qgbm1kcyBzb2x1dGlvbiByNiAxMDAwIHJlcCAtLT4NCjwhLS0gYGBge3IsIGV2YWw9VFJVRX0gLS0+DQo8IS0tICMgTG9hZGluZyBnZW9kaXN0IG9iamVjdCAtLT4NCjwhLS0gIyBnZW9kaXN0X25maSA8LSByZWFkUkRTKGZpbGUgPSAiLi4vU3BsaXRSZXYyX2dlb2Rpc3RfbmZpLnJkcyIpIC0tPg0KDQo8IS0tICMgTG9hZGluZyBtZHMgcmVzdWx0cyAtLT4NCjwhLS0gIyBtZHMgPC0gcmVhZFJEUyhmaWxlID0gIi4uL1NwbGl0UmV2Ml9tZHMucmRzIikgLS0+DQoNCjwhLS0gIyMgRXh0cmFjdGluZyB0aGUgc3RyZXNzIG9mIGVhY2ggbm1kcyBpdGVyYXRpb24gLS0+DQo8IS0tIG1kc19zdHJlc3NfcjZfMTAwMDwtdW5saXN0KGxhcHBseShtZHNfcjZfMTAwMCwgZnVuY3Rpb24odil7dltbMjJdXX0pKSAjYWRlZWQgMTAwMCAtLT4NCg0KPCEtLSBvcmRlcmVkX3I2XzEwMDAgPC1vcmRlcihtZHNfc3RyZXNzX3I2XzEwMDApIC0tPg0KDQo8IS0tICMjIEJlc3QsIHNlY29uZCBiZXN0LCBhbmQgd29yc3Qgc29sdXRpb24gLS0+DQo8IS0tIG1kc19zdHJlc3NfcjZfMTAwMFtvcmRlcmVkX3I2XzEwMDBbMV1dIC0tPg0KPCEtLSBtZHNfc3RyZXNzX3I2XzEwMDBbb3JkZXJlZF9yNl8xMDAwWzJdXSAtLT4NCjwhLS0gbWRzX3N0cmVzc19yNl8xMDAwW29yZGVyZWRfcjZfMTAwMFsxMDBdXSAtLT4NCg0KPCEtLSAjIyBTY2FsaW5nIG9mIGF4ZXMgdG8gaGFsZiBjaGFuZ2UgdW5pdHMgYW5kIHZhcmltYXggcm90YXRpb24gYnkgcG9zdE1EUyAtLT4NCjwhLS0gbWRzX2Jlc3RfcjZfMTAwMDwtcG9zdE1EUyhtZHNfcjZfMTAwMFtbb3JkZXJlZF9yNl8xMDAwWzFdXV0sIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICBnZW9kaXN0X3I2LCAgLS0+DQo8IS0tICAgICAgICAgICAgICAgICAgICAgIHBjID0gVFJVRSwgIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICBoYWxmY2hhbmdlID0gVFJVRSwgIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICB0aHJlc2hvbGQgPSBlcCkgICAgICMgSXMgdGhpcyB0aHJlc2hvbGQgcmVsYXRlZCB0byB0aGUgZXBzaWxvbiBhYm92ZT8gLS0+DQo8IS0tIG1kc19iZXN0X3I2XzEwMDAgLS0+DQoNCjwhLS0gbWRzX3NlY2Jlc3RfcjZfMTAwMDwtcG9zdE1EUyhtZHNfcjZfMTAwMFtbb3JkZXJlZF9yNl8xMDAwWzJdXV0sIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICBnZW9kaXN0X3I2LCAgLS0+DQo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgIHBjID0gVFJVRSwgIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICBoYWxmY2hhbmdlID0gVFJVRSwgIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICB0aHJlc2hvbGQgPSBlcCkgLS0+DQo8IS0tIG1kc19zZWNiZXN0X3I2XzEwMDAgLS0+DQoNCjwhLS0gIyMgUHJvY3J1c3RlcyBjb21wYXJpc29ucyAtLT4NCjwhLS0gcHJvY3JfcjZfMTAwMCA8LSBwcm9jcnVzdGVzKG1kc19iZXN0X3I2XzEwMDAsIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgIG1kc19zZWNiZXN0X3I2XzEwMDAsIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgIHBlcm11dGF0aW9ucz05OTkpIC0tPg0KPCEtLSBwcm90ZXN0KG1kc19iZXN0X3I2XzEwMDAsIC0tPg0KPCEtLSAgICAgICAgIG1kc19zZWNiZXN0X3I2XzEwMDAsIC0tPg0KPCEtLSAgICAgICAgIHBlcm11dGF0aW9ucz05OTkpIC0tPg0KDQo8IS0tIHBsb3QocHJvY3JfcjZfMTAwMCkgLS0+DQoNCjwhLS0gcG5nKGZpbGUucGF0aChkYXRhUGF0aCwib3V0cHV0cy9wcm9jcnVzdGVzX3I2XzEwMDAucG5nIiksIHdpZHRoPTEwMDAsIGhlaWdodD03MDAsKSAjYWRkZWQgMTAwMCAtLT4NCjwhLS0gcGxvdChwcm9jcl9yNl8xMDAwKSAtLT4NCjwhLS0gZGV2Lm9mZigpIC0tPg0KPCEtLSBgYGAgLS0+DQoNCg0KDQojIyMjIyBDb3JyZWxhdGlvbiBvZiBheGlzOiBEQ0EgdnMgTk1EUyANCnJldGFpbiB0aGUgMjAwIHJlcCB2ZXJzaW9uDQpgYGB7cn0NCiMgRXh0cmFjdGluZyBvcmRpbmF0aW9uIGF4aXMNCmF4IDwtIDINCmF4aXNfcjYgPC0gY2JpbmQobWRzX2Jlc3RfcjYkcG9pbnRzLA0KICAgICAgICAgICAgICAgICBzY29yZXMoZGNhX3I2LA0KICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheSA9ICJzaXRlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICBvcmlnaW4gPSBUUlVFKVssIDE6YXhdKQ0KDQpnZ2NvcnIoYXhpc19yNiwgDQogICAgICAgbWV0aG9kPWMoImV2ZXJ5dGhpbmciLCJrZW5kYWxsIiksIA0KICAgICAgIGxhYmVsID0gVFJVRSwNCiAgICAgICBsYWJlbF9zaXplID0gMywgDQogICAgICAgbGFiZWxfY29sb3IgPSAiYmxhY2siLCAgDQogICAgICAgbmJyZWFrcyA9IDgsDQogICAgICAgbGFiZWxfcm91bmQgPSAzLA0KICAgICAgIGxvdyA9ICJyZWQiLA0KICAgICAgIG1pZCA9ICJ3aGl0ZSIsDQogICAgICAgaGlnaCA9ICJncmVlbiIpDQoNCmBgYA0KDQojIyMjIyBTYXZlIHRoZSBmaWd1cmUNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL0JhcmVudHNfYmVsb3cxMDBfY29ycmVsYXRpb25QQ0F2c05NRFNfcjYucG5nIiksDQogICAgICAgZGV2aWNlID0gInBuZyIsDQogICAgICAgZHBpPTMwMCApDQoNCiMgU3dpdGNoaW5nIGRpcmVjdGlvbiBvZiBOTURTMQ0KIyBtZHNfYmVzdCRwb2ludHNbLCAxXSA8LSAtbWRzX2Jlc3QkcG9pbnRzWywgMV0NCmBgYA0KDQojIyMgUGxvdHRpbmcgRENBICYgR05NRFMNCmBgYHtyfQ0KIyMgQWRkaW5nIHNjb3JlcyB0byBkYXRhIGZyYW1lDQpvdHVfNiRnbm1kczEgPC0gbWRzX2Jlc3RfcjYkcG9pbnRzWywgMV0NCm90dV82JGdubWRzMiA8LSBtZHNfYmVzdF9yNiRwb2ludHNbLCAyXQ0Kb3R1XzYkZGNhMSA8LSBzY29yZXMoZGNhX3I2LCBkaXNwbGF5ID0gInNpdGVzIiwgb3JpZ2luID0gVFJVRSlbLCAxXQ0Kb3R1XzYkZGNhMiA8LSBzY29yZXMoZGNhX3I2LCBkaXNwbGF5ID0gInNpdGVzIiwgb3JpZ2luID0gVFJVRSlbLCAyXQ0KDQpwX2dubWRzX3I2IDwtIGdncGxvdChkYXRhID0gb3R1XzYsDQogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGdubWRzMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZ25tZHMyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBjb29yZF9maXhlZCgpICsNCiAgZ2d0aXRsZSgiR05NRFMiLA0KICAgICAgICAgIHN1YnRpdGxlID0gIkZpcnN0IHJ1biIpICsNCiAgZ2VvbV9wb2ludChjb2xvdXIgPSAicmVkIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHRncmF5IikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHRncmF5IikNCg0KcF9kY2FfcjYgPC0gZ2dwbG90KGRhdGEgPSBvdHVfNiwNCiAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGRjYTEsDQogICAgICAgICAgICAgICAgICAgICAgIHkgPSBkY2EyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBjb29yZF9maXhlZCgpICsNCiAgZ2d0aXRsZSgiRENBIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCBydW4iKSArDQogIGdlb21fcG9pbnQoY29sb3VyID0gInJlZCIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpDQoNCnBfZ25tZHNfcjYgKyBwX2RjYV9yNg0KDQoNCmBgYA0KDQojIyMjIyBTYXZlIHRoZSBmaWd1cmUNCmBgYHtyfQ0KZ2dzYXZlKGZpbGUucGF0aChkYXRhUGF0aCwib3V0cHV0cy9CYXJlbnRzX2JlbG93MTAwX2dubWRzX2RjYV9yNi5wbmciKSwNCiAgICAgICBkZXZpY2UgPSAicG5nIiwgDQogICAgICAgZHBpPTMwMCkNCmBgYA0KDQoNCiMjIFNwZWNpZXMtZW52aXJvbm1lbnQgcmVsYXRpb25zaGlwcw0KIyMjIFNlbGVjdGluZyBvcmRpbmF0aW9uDQpgYGB7cn0NCm9yZCA8LSBtZHNfYmVzdF9yNg0KDQojIyBBeGlzIHNjb3JlcyBpZiBzZWxlY3RlZCBvcmQgaXMgR05NRFMNCmF4aXMgPC0gb3JkJHBvaW50cyAlPiUgYXMuZGF0YS5mcmFtZQ0KDQojIyBBeGlzIHNjb3JlcyBpZiBzZWxlY3RlZCBvcmQgaXMgRENBDQojIGF4aXMgPC0gc2NvcmVzKG9yZCwNCiMgICAgICAgICAgICAgICAgZGlzcGxheSA9ICJzaXRlcyIsDQojICAgICAgICAgICAgICAgIG9yaWdpbiA9IFRSVUUpWywgMTpheF0pDQoNCmBgYA0KDQojIyMgQ3JlYXRlIGFkZGl0aW9uYWwgdmFyaWFibGVzDQpkZWNpZGVkIHRvIG1ha2UgTUxELWJhdGh5IHZhcnMNCmBgYHtyfQ0KZW52PC1lbnYgJT4lIA0KICBtdXRhdGUgKCJNTERtZWFuX2JhdGh5Ij1NTERtZWFuX1JvYmluc29uLShiYXRoeSotMSksDQogICAgICAgICAgIk1MRG1pbl9iYXRoeSI9TUxEbWluX1JvYmluc29uLShiYXRoeSotMSksDQogICAgICAgICAgIk1MRG1heF9iYXRoeSI9TUxEbWF4X1JvYmluc29uLShiYXRoeSotMSkpDQoNCmVudiRNTERtZWFuX2JhdGh5PC1jdXQoZW52JE1MRG1lYW5fYmF0aHksIA0KICAgICAgYnJlYWtzPWMoLTI1NjAsIC0yMCwyMCwxMzApLCNjaGVja2VkIHJhbmdlIG9mIHZhbHVlcyBmaXJzdCAobWluIC0yNTU0LCBtYXggMTIzKQ0KICAgICAgbGFiZWxzPWMoJ2JlbG93TUxEJywnb25QeWNubycsJ2luTWl4TGF5ZXInKSkNCmVudiRNTERtaW5fYmF0aHk8LWN1dChlbnYkTUxEbWluX2JhdGh5LCANCiAgICAgIGJyZWFrcz1jKC0yNTYwLCAtMjAsMjAsMTMwKSwjY2hlY2tlZCByYW5nZSBvZiB2YWx1ZXMgZmlyc3QgKG1pbiAtMjU1NCwgbWF4IDEyMykNCiAgICAgIGxhYmVscz1jKCdiZWxvd01MRCcsJ29uUHljbm8nLCdpbk1peExheWVyJykpDQplbnYkTUxEbWF4X2JhdGh5PC1jdXQoZW52JE1MRG1heF9iYXRoeSwgDQogICAgICBicmVha3M9YygtMjU2MCwgLTIwLDIwLDEzMCksI2NoZWNrZWQgcmFuZ2Ugb2YgdmFsdWVzIGZpcnN0IChtaW4gLTI1NTQsIG1heCAxMjMpDQogICAgICBsYWJlbHM9YygnYmVsb3dNTEQnLCdvblB5Y25vJywnaW5NaXhMYXllcicpKQ0KDQplbnYkc3dEZW5zUm9iX2F2czwtc3dSaG8oc2FsaW5pdHk9ZW52JFNtZWFuX1JvYmluc29uLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRlbXBlcmF0dXJlPWVudiRUbWVhbl9Sb2JpbnNvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBwcmVzc3VyZT0oZW52JGJhdGh5Ki0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBlb3M9InVuZXNjbyIpDQoNCmBgYA0KDQoNCg0KIyMjIENvcnJlbGF0aW9uIG9yZGluYXRpb24gYXhlcyBhbmQgZW52aXJvbm1lbnRhbCB2YXJpYWJsZXMNCiMjIyMgUmVtb3Zpbmcgbm9uLWVudiB2YXJzDQpgYGB7cn0NCmVudl9jb250PC1lbnYlPiUgc2VsZWN0KC1jKGxhbmRzY2FwZSxzZWRjbGFzcyxnbW9ycGgsIE1MRG1lYW5fYmF0aHksIE1MRG1heF9iYXRoeSwgTUxEbWluX2JhdGh5LCAjY2F0ZWdvcmljYWwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdGlvbmFsLCAjbm90IGEgdmFyDQogICAgICAgICAgICAgICAgICAgICAgICAgICBNTERtYXhfUm9iaW5zb24sIE1MRG1lYW5fUm9iaW5zb24sIE1MRG1pbl9Sb2JpbnNvbiwgICNyZXBsYWNlZCBieSBuZXcgdmFycw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgTUxEc2RfUm9iaW5zb24gI25vdCBtZWFuaW5nZnVsDQogICAgICAgICAgICAgICAgICAgICAgICAgICApKQ0KZW52X2NvbnQ8LWVudl9jb250JT4lIG11dGF0ZV9pZihpcy5pbnRlZ2VyLGFzLm51bWVyaWMpDQoNCmVudl9jb3JyIDwtIGVudl9jb250ICMgJT4lIHNlbGVjdCgtYyhTYW1wSUQpKQ0KDQojIGVudl9jb3JyJGNvb3Jkcy54MTwtYXMubnVtZXJpYyhlbnZfY29yciRjb29yZHMueDEpDQojIGVudl9jb3JyJGNvb3Jkcy54MjwtYXMubnVtZXJpYyhlbnZfY29yciRjb29yZHMueDIpDQoNCmVudl9jb3JyWyghaXMubnVtZXJpYyhlbnZfY29ycikpLF0NCmBgYA0KDQojIyMjIENvcnJlbGF0aW9ucw0KYGBge3J9DQojIFZlY3RvciB0byBob2xkIGNvcnJlbGF0aW9ucw0KY29yX2F4MSA8LSBOVUxMDQpjb3JfYXgyIDwtIE5VTEwNCnB2X2F4MSA8LSBOVUxMDQpwdl9heDIgPC0gTlVMTA0KDQojIE5NRFMxDQpmb3IoIGkgaW4gc2VxKGxlbmd0aChlbnZfY29ycikpKSB7DQogIGN0LmkgPC0gY29yLnRlc3QoYXhpcyRNRFMxLA0KICAgICAgICAgICAgICAgICAgIGVudl9jb3JyWywgaV0sDQogICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImtlbmRhbGwiKQ0KICBjb3JfYXgxW2ldIDwtIGN0LmkkZXN0aW1hdGUNCiAgcHZfYXgxW2ldIDwtIGN0LmkkcC52YWx1ZQ0KfQ0KDQojIE5NRFMyDQpmb3IoIGkgaW4gc2VxKGxlbmd0aChlbnZfY29ycikpKSB7DQogIGN0LmkgPC0gY29yLnRlc3QoYXhpcyRNRFMyLA0KICAgICAgICAgICAgICAgICAgIGVudl9jb3JyWywgaV0sDQogICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImtlbmRhbGwiKQ0KICBjb3JfYXgyW2ldIDwtIGN0LmkkZXN0aW1hdGUNCiAgcHZfYXgyW2ldIDwtIGN0LmkkcC52YWx1ZQ0KfQ0KDQpjb3JfdGFiIDwtIGRhdGEuZnJhbWUoZW52ID0gbmFtZXMoZW52X2NvcnIpLA0KICAgICAgICAgICAgICAgICAgICAgIG9yZF9heDEgPSBjb3JfYXgxLA0KICAgICAgICAgICAgICAgICAgICAgIHB2YWxfYXgxID0gcHZfYXgxLA0KICAgICAgICAgICAgICAgICAgICAgIG9yZF9heDIgPSBjb3JfYXgyLA0KICAgICAgICAgICAgICAgICAgICAgIHB2YWxfYXgyID0gcHZfYXgyKQ0KDQpjb3JfdGFiDQoNCndyaXRlLmNzdih4ID0gY29yX3RhYiwNCiAgICAgICAgICBmaWxlID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJpbnB1dHMvQmFyZW50c19iZWxvdzEwMF9jb3ItdGFibGVfcjZfMjAwcmVwX01MRC1iYXRoeS5jc3YiKSwNCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KIyMjIERvdCBjaGFydCB0byBjaGVjayBmb3IgZ2FwcyBpbiBjb3JyZWxhdGlvbg0KDQpgYGB7cn0NCmNvcl9hMV9zb3J0PC1jb3JfdGFiJT4lDQogIG11dGF0ZShhYnNfb3JkX2F4MT1hYnMob3JkX2F4MSksDQogICAgICAgICBhYnNfb3JkX2F4Mj1hYnMob3JkX2F4MikpICU+JQ0KICBhcnJhbmdlKGRlc2MoYWJzX29yZF9heDEpKQ0KDQpjb3JfYTJfc29ydDwtY29yX3RhYiU+JQ0KICBtdXRhdGUoYWJzX29yZF9heDE9YWJzKG9yZF9heDEpLA0KICAgICAgICAgYWJzX29yZF9heDI9YWJzKG9yZF9heDIpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGFic19vcmRfYXgyKSkNCg0KZG90Y2hhcnQoY29yX2ExX3NvcnQkYWJzX29yZF9heDEsIG1haW49IkFic29sdXRlICgrLy0pIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIGVudlZhcnMgYW5kIGdubWRzIGF4aXMgMSIpDQoNCmNvcl9jdXQ8LTAuMzggI2RlY2lkZQ0KDQpjb3Jfc2VsPC1zdWJzZXQoY29yX2ExX3NvcnQsYWJzX29yZF9heDE+Y29yX2N1dCkNCmNvcl9zZWwNCg0KYXMuZGF0YS5mcmFtZShjb3Jfc2VsJGVudikNCmBgYA0KDQoNCiMjIyBTZWwgZW52IHZhciAodG9wIGNvcnIpDQpgYGB7cn0NCmVudl9vcyA8LSBlbnZbLCBjb3Jfc2VsJGVudl0NCmVudl9vcw0Kc3RyKGVudl9vcykNCg0KDQpgYGANCg0KDQojIyMgT3JkaXN1cmZzIHRvcCBjb3JyDQpgYGB7cn0NCm9yZHNyZnMgPC0gbGlzdChsZW5ndGggPSBuY29sKGVudl9vcykpDQoNCmZvciAoaSBpbiBzZXEobmNvbChlbnZfb3MpKSkgew0KICBvcy5pIDwtIGdnX29yZGlzdXJmKG9yZCA9IG9yZCwNCiAgICAgICAgICAgICAgICAgICAgICBlbnYudmFyID0gZW52X29zWywgaV0sDQogICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgIyBiaW53aWR0aCA9IDAuMDUsDQogICAgICAgICAgICAgICAgICAgICAgdmFyLmxhYmVsID0gbmFtZXMoZW52X29zKVtpXSwNCiAgICAgICAgICAgICAgICAgICAgICBnZW4udGV4dC5zaXplID0gMTAsDQogICAgICAgICAgICAgICAgICAgICAgdGl0bGUudGV4dC5zaXplID0gMTUsDQogICAgICAgICAgICAgICAgICAgICAgbGVnLnRleHQuc2l6ZSA9IDEwKQ0KICANCiAgb3Jkc3Jmc1tbaV1dIDwtIG9zLmkkcGxvdA0KfQ0KDQpvcmRzcmZzX3BsdCA8LSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBvcmRzcmZzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSA0LA0KICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSA1KQ0KDQpvcmRzcmZzX3BsdA0KYGBgDQojIyMjIyBTYXZlIHNvbWUgb3V0cHV0cw0KYGBge3J9DQpnZ2V4cG9ydChvcmRzcmZzX3BsdCwNCiAgICAgICAgICBmaWxlbmFtZSA9IGZpbGUucGF0aChkYXRhUGF0aCwib3V0cHV0cy9CYXJlbnRzX2JlbG93MTAwX29yZGlzdXJmc190b3BfY29yci5wbmciKSwNCiAgICAgICAgICB3aWR0aCA9IDIwMDAsDQogICAgICAgICAgaGVpZ2h0ID0gMjUwMCkNCg0KYGBgDQoNCg0KIyMjIFNlbCBlbnYgdmFyIChtYW51YWwpDQpgYGB7cn0NCmVudl9vc19tIDwtIGVudlssYygiVG1lYW5fUm9iaW5zb24iLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzYWx0X21heCIsICN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIlNtYXhfUm9iaW5zb24iLCAjY29tcGFyaXNvbiB0byB0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgInN3RGVuc1JvYl9hdnMiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJCTzIyX2ljZWNvdmVybHRtYXhfc3MiLCN0b3AgY29yciBheDINCiAgICAgICAgICAgICAgICAgICJCTzIyX2ljZWNvdmVybWVhbl9zcyIsI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAiQk8yMl9kaXNzb3htZWFuX2JkbWVhbiIsI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAjIkJPMjJfY2FyYm9ucGh5dG9sdG1pbl9iZG1lYW4iLCN0b3AgY29yciAtIG5vIGNsZWFyIGdyYWRpZW50IGluIG9yZGlzdXJmDQogICAgICAgICAgICAgICAgICAiQk8yMl9wcGx0bWluX3NzIiwgI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAiWC55IiwgI2NvbXBhcmlzb24gdG8gWQ0KICAgICAgICAgICAgICAgICAgIlkiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzcGRfc3RkIiwgI3RvcCBjb3JyIGF4MiAoYmxlbmRlZCBtb2RlbCkNCiAgICAgICAgICAgICAgICAgICJDU3Bkc2RfUm9iaW5zb24iLCAjY29tcGFyaXNvbiB0byB0b3AgY29yciBheDIgKGJsZW5kZWQgbW9kZWwpDQogICAgICAgICAgICAgICAgICAibXVkIiwgI2hpZ2hlc3Qgc2VkIHZhciBheDEgKyBjb3JyDQogICAgICAgICAgICAgICAgICAiZ3JhdmVsIiwjaGlnaGVzdCBzZWQgdmFyIGF4MSAtIGNvcnINCiAgICAgICAgICAgICAgICAgICJCTzIyX3NpbGljYXRlbHRtYXhfYmRtZWFuIiwgI2p1c3QgdW5kZXIgdG9wIGNvcnIgYXgxDQogICAgICAgICAgICAgICAgICAiYmF0aHkiICNpbnR1aXRpdmUgZm9yIGNvbXBhcmlzb25zDQogICAgICAgICAgICAgICAgKV0NCmVudl9vc19tDQpzdHIoZW52X29zX20pDQoNCmBgYA0KDQoNCiMjIyBPcmRpc3VyZnMgbWFudWFsbHkgc2VsZWN0ZWQNCmBgYHtyfQ0Kb3Jkc3Jmc19tIDwtIGxpc3QobGVuZ3RoID0gbmNvbChlbnZfb3NfbSkpDQoNCmZvciAoaSBpbiBzZXEobmNvbChlbnZfb3NfbSkpKSB7DQogIG9zLmlfbSA8LSBnZ19vcmRpc3VyZihvcmQgPSBvcmQsDQogICAgICAgICAgICAgICAgICAgICAgZW52LnZhciA9IGVudl9vc19tWywgaV0sDQogICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgIyBiaW53aWR0aCA9IDAuMDUsDQogICAgICAgICAgICAgICAgICAgICAgdmFyLmxhYmVsID0gbmFtZXMoZW52X29zX20pW2ldLA0KICAgICAgICAgICAgICAgICAgICAgIGdlbi50ZXh0LnNpemUgPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgICB0aXRsZS50ZXh0LnNpemUgPSAxNSwNCiAgICAgICAgICAgICAgICAgICAgICBsZWcudGV4dC5zaXplID0gMTApDQogIA0KICBvcmRzcmZzX21bW2ldXSA8LSBvcy5pX20kcGxvdA0KfQ0KDQpvcmRzcmZzX3BsdF9tIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IG9yZHNyZnNfbSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gNCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gNCkNCg0Kb3Jkc3Jmc19wbHRfbQ0KYGBgDQoNCiMjIyMjIFNhdmUgc29tZSBvdXRwdXRzDQpgYGB7cn0NCmdnZXhwb3J0KG9yZHNyZnNfcGx0X20sDQogICAgICAgICAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsIm91dHB1dHMvQmFyZW50c19iZWxvdzEwMF9vcmRpc3VyZnNfbWFuX3NlbF9kb21lYW4ucG5nIiksDQogICAgICAgICAgd2lkdGggPSAyMDAwLA0KICAgICAgICAgIGhlaWdodCA9IDIwMDApDQoNCmBgYA0KDQoNCg0KDQojIyMgRW52Zml0DQpgYGB7cn0NCiMjIFNlbGVjdCBpZiBhbnkgdmFyIHNob3VsZCBiZSBleGNsdWRlZCBmcm9tIGVudmZpdCAobWFrZXMgbGVzcyBidXN5IHRvIHJlYWQpDQplbnZfb3NfbV9lbnZmaXQ8LWVudl9vc19tIFssYygiVG1lYW5fUm9iaW5zb24iLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzYWx0X21heCIsICN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIlNtYXhfUm9iaW5zb24iLCAjY29tcGFyaXNvbiB0byB0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgInN3RGVuc1JvYl9hdnMiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJCTzIyX2ljZWNvdmVybHRtYXhfc3MiLCN0b3AgY29yciBheDINCiAgICAgICAgICAgICAgICAgICMiQk8yMl9pY2Vjb3Zlcm1lYW5fc3MiLCN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIkJPMjJfZGlzc294bWVhbl9iZG1lYW4iLCN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIyJCTzIyX2Rpc3NveGx0bWluX2JkbWVhbiIsI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAjIkJPMjJfY2FyYm9ucGh5dG9sdG1pbl9iZG1lYW4iLCN0b3AgY29yciAtIG5vIGNsZWFyIGdyYWRpZW50IGluIG9yZGlzdXJmDQogICAgICAgICAgICAgICAgICAiQk8yMl9wcGx0bWluX3NzIiwgI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAiWC55IiwgI2NvbXBhcmlzb24gdG8gWQ0KICAgICAgICAgICAgICAgICAgIlkiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzcGRfc3RkIiwgI3RvcCBjb3JyIGF4MiAoYmxlbmRlZCBtb2RlbCkNCiAgICAgICAgICAgICAgICAgIyAiQ1NwZHNkX1JvYmluc29uIiwgI2NvbXBhcmlzb24gdG8gdG9wIGNvcnIgYXgyIChibGVuZGVkIG1vZGVsKQ0KICAgICAgICAgICAgICAgICAgIm11ZCIsICNoaWdoZXN0IHNlZCB2YXIgYXgxICsgY29ycg0KICAgICAgICAgICAgICAgICAgImdyYXZlbCIsI2hpZ2hlc3Qgc2VkIHZhciBheDEgLSBjb3JyDQogICAgICAgICAgICAgICAgICAiQk8yMl9zaWxpY2F0ZWx0bWF4X2JkbWVhbiIsICNqdXN0IHVuZGVyIHRvcCBjb3JyIGF4MQ0KICAgICAgICAgICAgICAgICAgImJhdGh5IiAjaW50dWl0aXZlIGZvciBjb21wYXJpc29ucw0KICAgICAgICAgICAgICAgIA0KICANCildDQoNCmNvbG5hbWVzKGVudl9vc19tX2VudmZpdCk8LWMoIlQiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzTXgiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJTbWF4UiIsICNjb21wYXJpc29uIHRvIHRvcCBjb3JyDQogICAgICAgICAgICAgICAgICAic3dEZW5zUiIsICN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgImljZWNvdm1heCIsI3RvcCBjb3JyIGF4Mg0KICAgICAgICAgICAgICAgICAgIyJpY2Vjb3ZhdiIsI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAiZGlzc294YXYiLCN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIyJkaXNzb3htaW4iLCN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIyJCTzIyX2NhcmJvbnBoeXRvbHRtaW5fYmRtZWFuIiwjdG9wIGNvcnIgLSBubyBjbGVhciBncmFkaWVudCBpbiBvcmRpc3VyZg0KICAgICAgICAgICAgICAgICAgInBwbHRtaW4iLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJYIiwgI2NvbXBhcmlzb24gdG8gWQ0KICAgICAgICAgICAgICAgICAgIlkiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzcGRfc3RkIiwgI3RvcCBjb3JyIGF4MiAoYmxlbmRlZCBtb2RlbCkNCiAgICAgICAgICAgICAgICAgIyAiQ1NzZCIsICNjb21wYXJpc29uIHRvIHRvcCBjb3JyIGF4MiAoYmxlbmRlZCBtb2RlbCkNCiAgICAgICAgICAgICAgICAgICJtdWQiLCAjaGlnaGVzdCBzZWQgdmFyIGF4MSArIGNvcnINCiAgICAgICAgICAgICAgICAgICJncmF2ZWwiLCNoaWdoZXN0IHNlZCB2YXIgYXgxIC0gY29ycg0KICAgICAgICAgICAgICAgICAgIlNpTHRtYXgiLCAjanVzdCB1bmRlciB0b3AgY29yciBheDENCiAgICAgICAgICAgICAgICAgICJiYXRoeSIgI2ludHVpdGl2ZSBmb3IgY29tcGFyaXNvbnMNCiAgICAgICAgICAgICAgICAgKQ0KDQojIyBFbnZmb3QgcGxvdA0KZ2dfZW52Zml0KG9yZCA9IG9yZCwNCiAgICAgICAgICBlbnYgPSBlbnZfb3NfbV9lbnZmaXQsDQogICAgICAgICAgcHQuc2l6ZSA9IDEpDQoNCiMjIEVudmZpdCBhbmFseXNpcw0KDQplZiA8LSBlbnZmaXQob3JkID0gb3JkLA0KICAgICAgICAgICAgIGVudiA9IGVudl9vc19tX2VudmZpdCwNCiAgICAgICAgICAgICAjIG5hLnJtID0gVFJVRQ0KICAgICAgICAgICAgICkNCmVmREYgPC0gYXMuZGF0YS5mcmFtZShzY29yZXMoZWYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXkgPSAidmVjdG9ycyIpKQ0KYGBgDQoNCiMjIyMjIFNhdmUgdGhlIHBsb3QNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL0JhcmVudHNfYmVsb3cxMDBfRW52Rml0X21hbl9zZWxfY2xuX2RvbWVhbi5wbmciKSwNCiAgICAgICBkZXZpY2UgPSAicG5nIiwNCiAgICAgICBkcGk9MzAwICkNCmBgYA0KIyMjIENhdGVnb3JpY2FsIGVudlZhciB2aXN1YWxpc2VkIG9uIHRoZSBtZHNwbG90cw0KDQojIyMjIHVzZSBkYXRhc2V0IHdpdGggY2F0ZWdvcmljYWwgdmFyIGluY2x1ZGVkDQoNCmBgYHtyfQ0KI1NhbXBJRCA8LSBlbnZfc29ydCRTYW1wSUQNCg0KZW52X3ZpczwtZW52DQplbnZfdmlzJGdubWRzMSA8LSBvdHVfNiRnbm1kczENCmVudl92aXMkZ25tZHMyIDwtIG90dV82JGdubWRzMg0KDQplbnZfdmlzJGRjYTEgPC0gb3R1XzYkZGNhMQ0KZW52X3ZpcyRkY2EyIDwtIG90dV82JGRjYTINCmBgYA0KDQojIyMjIGdubWRzIHcgbWxkIG1lYW4gLSBiYXRoeQ0KYGBge3J9DQpkY2FfaW50IDwtIGdncGxvdChkYXRhID0gZW52X3ZpcywNCiAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gZGNhMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZGNhMikpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGdndGl0bGUoIkludGVyYWN0aXZlIERDQSBzYW1wbGUgSUQgcGxvdCIsDQogICAgICAgICAgc3VidGl0bGUgPSAiRmlyc3QgcnVuIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBmYWN0b3IoU2FtcElEKSkpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpDQoNCmdncGxvdGx5KGRjYV9pbnQpDQpgYGANCg0KDQoNCg0KDQojIyMjIGdubWRzIHcgbWxkIG1lYW4gLSBiYXRoeQ0KYGBge3J9DQpwX21sZCA8LSBnZ3Bsb3QoZGF0YSA9IGVudl92aXMsDQogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGdubWRzMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZ25tZHMyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBjb29yZF9maXhlZCgpICsNCiAgZ2d0aXRsZSgiR05NRFMgY29sb3VyZWQgYnkgcHJveGltaXR5IHRvIG1peGVkIGxheWVyIGRlcHRoIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCBydW4iKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IE1MRG1lYW5fYmF0aHkpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKQ0KDQpwX21sZA0KYGBgDQoNCiMjIyMgZ25tZHMgdyBzZWRjbGFzcw0KIyMjIyMgZGVhbCB3aXRoIHNlZGNsYXNzIGNvZGVzDQpgYGB7cn0NCmVudl92aXM8LSBlbnZfdmlzICU+JSANCiAgbXV0YXRlKA0KICAgIHNlZGNsYXNzTmFtZSA9IGNhc2Vfd2hlbigNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIxIiB+ICJTZWRDb3ZlclIiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjUiIH4gIlJvY2siLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjIwIiB+ICJNdWQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjIxIiB+ICJNd0Jsb2NrIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICI0MCIgfiAic011ZCIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiODAiIH4gIm1TYW5kIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIxMDAiIH4gIlNhbmQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjExMCIgfiAiZ011ZCIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiMTE1IiB+ICJnc011ZCIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiMTIwIiB+ICJnbVNhbmQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjEzMCIgfiAiZ1NhbmQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjE1MCIgfiAiTVNHIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIxNjAiIH4gInNHcmF2ZWwiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjE3MCIgfiAiR3JhdmVsIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIxNzUiIH4gIkdyYXZCbG9jayIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiMTg1IiB+ICJTR0JtaXgiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjIwNSIgfiAiUy9Nd0IiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjIwNiIgfiAiUy9Nd0cvQiIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiMjE1IiB+ICJTR0JhbHQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjMwMCIgfiAiSGFyZFNlZCIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiNTAwIiB+ICJCaW9nZW5pYyINCiAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICkNCiAgKQ0KYGBgDQoNCiMjIyMjIyBjb2xvdXIgcGFsZXR0ZSB0byBjb3BlIHdpdGggdXAgdG8gMjUgY2F0ZWdvcmljYWwgY29sb3Vycw0KYGBge3J9DQpjMjUgPC0gYygNCiAgImRvZGdlcmJsdWUyIiwgIiNFMzFBMUMiLCAjIHJlZA0KICAiZ3JlZW40IiwNCiAgIiM2QTNEOUEiLCAjIHB1cnBsZQ0KICAiI0ZGN0YwMCIsICMgb3JhbmdlDQogICJibGFjayIsICJnb2xkMSIsDQogICJza3libHVlMiIsICIjRkI5QTk5IiwgIyBsdCBwaW5rDQogICJwYWxlZ3JlZW4yIiwNCiAgIiNDQUIyRDYiLCAjIGx0IHB1cnBsZQ0KICAiI0ZEQkY2RiIsICMgbHQgb3JhbmdlDQogICJncmF5NzAiLCAia2hha2kyIiwNCiAgIm1hcm9vbiIsICJvcmNoaWQxIiwgImRlZXBwaW5rMSIsICJibHVlMSIsICJzdGVlbGJsdWU0IiwNCiAgImRhcmt0dXJxdW9pc2UiLCAiZ3JlZW4xIiwgInllbGxvdzQiLCAieWVsbG93MyIsDQogICJkYXJrb3JhbmdlNCIsICJicm93biINCikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCg0KcF9zZWQgPC0gZ2dwbG90KGRhdGEgPSBlbnZfdmlzLA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBnbm1kczEsDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdubWRzMikpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGdndGl0bGUoIkdOTURTIGNvbG91cmVkIGJ5IHNlZGltZW50IGNsYXNzIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCBydW4iKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGZhY3RvcihzZWRjbGFzc05hbWUpKSkgKw0KICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9YzI1KSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpKw0KICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZChuY29sPTIpKQ0KDQpwX3NlZA0KYGBgDQoNCiMjIyMgZ25tZHMgdyBsYW5zY2FwZQ0KIyMjIyMgZGVhbCB3aXRoIHNlZGNsYXNzIGNvZGVzDQpgYGB7cn0NCmVudl92aXM8LSBlbnZfdmlzICU+JSANCiAgbXV0YXRlKA0KICAgIGxhbmRzY2FwZU5hbWUgPSBjYXNlX3doZW4oDQogICAgICAgICAgICBsYW5kc2NhcGUgPT0gIjEiIH4gIlN0cmFuZGZsYXQiLA0KICAgICAgICAgICAgbGFuZHNjYXBlID09ICIyMSIgfiAiQ29udFNsb3BlIiwNCiAgICAgICAgICAgIGxhbmRzY2FwZSA9PSAiMjIiIH4gIkNhbnlvbiIsDQogICAgICAgICAgICBsYW5kc2NhcGUgPT0gIjMxIiB+ICJWYWxsZXkiLA0KICAgICAgICAgICAgbGFuZHNjYXBlID09ICIzMiIgfiAiRmpvcmQiLA0KICAgICAgICAgICAgbGFuZHNjYXBlID09ICI0MSIgfiAiRGVlcFBsYWluIiwNCiAgICAgICAgICAgIGxhbmRzY2FwZSA9PSAiNDIiIH4gIlNsb3BlUGxhaW4iLA0KICAgICAgICAgICAgbGFuZHNjYXBlID09ICI0MyIgfiAiU2hlbGZQbGFpbiIsDQogICAgICAgICAgICBsYW5kc2NhcGUgPT0gIjQzMSIgfiAic2hhbGxvd1ZhbGxleSINCiAgICApDQogICkNCmBgYA0KDQoNCmBgYHtyfQ0KDQpwX2xhbmQgPC0gZ2dwbG90KGRhdGEgPSBlbnZfdmlzLA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBnbm1kczEsDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdubWRzMikpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGdndGl0bGUoIkdOTURTIGNvbG91cmVkIGJ5IGxhbmRzY2FwZSBjbGFzcyIsDQogICAgICAgICAgc3VidGl0bGUgPSAiRmlyc3QgcnVuIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBmYWN0b3IobGFuZHNjYXBlTmFtZSkpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKSsNCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQobmNvbD0yKSkNCg0KcF9sYW5kDQpgYGANCg0KDQojIyMjIEdubWRzIHcgZ21vcnBoDQpgYGB7cn0NCg0KcF9nbW8gPC0gZ2dwbG90KGRhdGEgPSBlbnZfdmlzLA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBnbm1kczEsDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdubWRzMikpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGdndGl0bGUoIkdOTURTIGNvbG91cmVkIGJ5IGxhbmRzY2FwZSBjbGFzcyIsDQogICAgICAgICAgc3VidGl0bGUgPSAiRmlyc3QgcnVuIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBmYWN0b3IoZ21vcnBoKSkpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpKw0KICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZChuY29sPTIpKQ0KDQpwX2dtbw0KYGBgDQoNCg0KYGBge3J9DQpjYXRfdmFyX3Bsb3RzPC1wX21sZCtwX3NlZCtwX2xhbmQrcF9nbW8NCmBgYA0KIyMjIyMgU2F2ZSB0aGUgcGxvdA0KYGBge3J9DQpnZ2V4cG9ydChjYXRfdmFyX3Bsb3RzLA0KICAgICAgICAgIGZpbGVuYW1lID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL0JhcmVudHNfZ25tZHNfY2F0dmFyLnBuZyIpLA0KICAgICAgICAgIHdpZHRoID0gMTAwMCwNCiAgICAgICAgICBoZWlnaHQgPSA4MDApDQpgYGANCg0KIyMjIyBTYW1wbGUgaWRlbnRpZmljYXRpb24gaW4gdGhlIG1kc3Bsb3QNCg0KYGBge3J9DQpwX2dtbyA8LSBnZ3Bsb3QoZGF0YSA9IGVudl92aXMsDQogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGdubWRzMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZ25tZHMyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBjb29yZF9maXhlZCgpICsNCiAgZ2d0aXRsZSgiR05NRFMgY29sb3VyZWQgYnkgc2FtcGxlIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCBydW4iKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGZhY3RvcihTYW1wSUQpKSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHRncmF5IikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHRncmF5IikrDQogIGd1aWRlcyhjb2xvdXI9Im5vbmUiLCBzaXplPSJub25lIikNCg0KZ2dwbG90bHkocF9nbW8pDQpgYGANCg0K